Load data files
library(dplyr)
# specify the directory where the files are located
# For NOAA machines
# dir_path <- "/Users/enrique.montes/Google Drive/My Drive/GDrive/OCED_AOML/WS_cruises/plankton_imaging/CPICS/TS.Master_selection"
# For personal machine
dir_path <- "~/Library/CloudStorage/GoogleDrive-enriquemontes01@gmail.com/My Drive/GDrive/OCED_AOML/WS_cruises/plankton_imaging/CPICS/TS.Master_selection"
# obtain a list of file names in the directory
file_names <- list.files(path = dir_path, pattern = ".txt", full.names = TRUE)
# loop over each file and import the tables (use this for DATES)
for (file in file_names) {
table_name <- gsub(".txt", "", basename(file)) # get the name of the table from the file name
assign(table_name, read.table(file = file, header = FALSE, sep = "\t") %>%
mutate(date = as.POSIXct(substr(V1, start = 24, stop = 36), format="%Y%m%d_%H%M", tz="UTC")))
}
# # loop over each file and import the tables (use this for STRINGS)
# for (file in file_names) {
# table_name <- gsub(".txt", "", basename(file)) # get the name of the table from the file name
# assign(table_name, read.table(file = file, header = FALSE, sep = "\t"))
# }
Match file name using DATE values
library(tidyverse)
library(lubridate)
dir_path2 <- "~/Library/CloudStorage/GoogleDrive-enriquemontes01@gmail.com/My Drive/GDrive/OCED_AOML/WS_cruises/plankton_imaging/CPICS/ws_cruise_ctd"
file_name <- list.files(path = dir_path2, pattern = "ctd_meta_v3.csv", full.names = TRUE)
ctd_meta <- read.csv(file_name, fill = TRUE)
# USE WITH ctd_meta_v3.csv
dt_list <- as.POSIXct(paste(ctd_meta$year,
sprintf("%02d", ctd_meta$month),
sprintf("%02d", ctd_meta$day),
ctd_meta$time_gmt),
format = "%Y%m%d %I:%M:%S %p",
tz = "UTC")
################################################################################################################
# This section detects short transit times between stations
# Calculate time differences in seconds between consecutive dt_list objects
time_differences <- as.numeric(difftime(dt_list[-1], dt_list[-length(dt_list)], units = "secs"))
# Convert time differences from seconds to minutes
time_differences_mins <- time_differences / 60
# Create a data frame showing the original times and their differences in minutes
time_diff_df <- data.frame(
start_time = dt_list[-length(dt_list)],
end_time = dt_list[-1],
time_difference_mins = time_differences_mins
)
# find CTD time stamps of consecutive stations within less than 20 min. This will identify CTD casts that are close to each other
short_t_idx <- which(time_diff_df$time_difference_mins < 20)
short_timestamps <- dt_list[short_t_idx]
#################################################################################################################
# Create empty data frame to store results
conc_occ_final <- data.frame(date = character(), count = numeric())
# List of class objects to be processed
class_names <- c("Acantharea", "Centric", "Ceratium", "Chaetoceros", "Chaetognaths",
"Chain2", "Chain3", "Ostracods", "Copepods", "Decapods", "Echinoderms",
"Guinardia", "Jellies", "Larvaceans", "Neocalyptrella", "Noctiluca",
"pellets", "Polychaets", "Pteropods", "Tricho")
# time buffer before and after CTD time in seconds so that CPICS records are matched to CTD times.
start <- 10 * 60
stop <- 10 * 60
# Iterate over dt_list intervals
for (i in 1:length(dt_list)) {
# Initialize a list to store counts for the current interval
counts_list <- list(date = dt_list[i])
# Iterate over each class object and perform subsetting
for (class_name in class_names) {
class_data <- get(paste0("class.", class_name)) # Dynamically get the class data frame
if (i < length(dt_list)) {
# Subsetting for all intervals except the last one
subset_data <- subset(class_data, date >= dt_list[i]-start & date < dt_list[i+1]-stop)
} else {
# Subsetting for the last interval: capture all data greater than or equal to the last dt_list
subset_data <- subset(class_data, date >= dt_list[i]-start)
}
counts_list[[class_name]] <- nrow(subset_data)
}
# Convert counts_list to a data frame and bind it to the result
result <- as.data.frame(counts_list)
conc_occ_final <- rbind(conc_occ_final, result)
}
# Combine with ctd_meta
taxa_meta <- cbind(ctd_meta, conc_occ_final)
################################################################################
# Check for unaccounted CPICS records
# Initialize a list to store unaccounted dates for each class
unaccounted_dates_list <- list()
# Iterate over each class object
for (class_name in class_names) {
# Get the date-time objects from the current class
sel_class_dates <- get(paste0("class.", class_name))$date
# Initialize a logical vector to track whether each class date is accounted for
is_accounted_for <- rep(FALSE, length(sel_class_dates))
# Check each class date against the intervals in dt_list
for (i in 1:length(dt_list)) {
if (i < length(dt_list)) {
# Check all intervals except the last one
interval_start <- dt_list[i] - start
interval_end <- dt_list[i + 1] - stop
} else {
# Last interval captures all data greater than or equal to the last dt_list
interval_start <- dt_list[i] - start
interval_end <- Inf # Effectively no upper bound
}
# Mark class dates that fall within the current interval as accounted for
is_accounted_for <- is_accounted_for | (sel_class_dates >= interval_start & sel_class_dates < interval_end)
}
# Subset the class dates that were not accounted for
unaccounted_sel_class_dates <- sel_class_dates[!is_accounted_for]
# Store the unaccounted dates in the list
unaccounted_dates_list[[class_name]] <- unaccounted_sel_class_dates
}
# Print or view the unaccounted dates for each class
for (class_name in class_names) {
cat("Unaccounted dates for class:", class_name, "\n")
print(unaccounted_dates_list[[class_name]])
cat("\n")
}
# Calculate plankton concentration time series per station
# Calculate species concentration (counts/ml) for each species
# # For zooplankton
# taxa_meta_concentration <- taxa_meta %>%
# mutate(across(c(Acantharea,Chaetognaths,Ostracods,Copepods,Decapods,Echinoderms,Jellies,
# Larvaceans,Polychaets,Pteropods), ~ ./total_vol_sampled * 1e6)) %>%
# select(Station, dec_lat, dec_lon, year, month, date, Avg.chl.a..ug.L.,
# temp..degC., salinity, total_vol_sampled,
# Acantharea,Chaetognaths,Ostracods,Copepods,Decapods,Echinoderms,
# Jellies,Larvaceans,Polychaets,Pteropods) %>%
# filter(!is.na(total_vol_sampled))
#
# # # Transform taxa_meta_concentration to long format
# taxa_meta_long <- taxa_meta_concentration %>%
# pivot_longer(cols = c(Acantharea, Chaetognaths,Ostracods,Copepods,Decapods,
# Echinoderms, Jellies, Larvaceans,Polychaets, Pteropods),
# names_to = "species", values_to = "species_concentration")
# For phytoplankton
taxa_meta_concentration <- taxa_meta %>%
mutate(across(c(Centric,Ceratium,Chaetoceros,Chain2,Chain3,Guinardia,Neocalyptrella,
Noctiluca,Tricho), ~ ./total_vol_sampled * 1e6)) %>%
select(Station, dec_lat, dec_lon, year, month, date, Avg.chl.a..ug.L.,
temp..degC., salinity,total_vol_sampled,
Centric,Ceratium,Chaetoceros,Chain2,Chain3,Guinardia,Neocalyptrella,
Noctiluca,Tricho) %>%
filter(!is.na(total_vol_sampled))
# # Transform taxa_meta_concentration to long format
taxa_meta_long <- taxa_meta_concentration %>%
pivot_longer(cols = c(Centric,Ceratium,Chaetoceros,Chain2,Chain3,Guinardia,Neocalyptrella,
Noctiluca,Tricho),
names_to = "species", values_to = "species_concentration")
# Filter the data for Stations
# station_list_fk <- c("WS","21/LK","MR","16","18","10","12","9.5","9","7","2")
# station_list_sr <- c("68","65","64","60","58","57.3","57.2","57.1","57","56","55","54","53","51",
# "49","47","45","41","30","31","33")
# station_list_cal <- c("CAL5","CAL4","CAL3","CAL2","CAL1","RP1","RP2","RP3","RP4","GP5","BG4","BG3",
# "BG2","BG1","BG6", "BG7")
# station_list_vl <- c("V1","V2","V3","V4","V5","V6","V7","V8","V9","L1","L3","L5","L7","L9")
# station_list_tb <- c("AMI9","AMI8","AMI7","AMI6","AMI5","AMI4","AMI3","AMI2","AMI1","TB1","TB2",
# "TB3","TB4","TB5","TB10","CW4","CW3","CW2","CW1")
station_selected <- "31"
filtered_taxa_meta_long <- taxa_meta_long %>%
filter(Station %in% station_selected)
# # Without filtering per group of stations but looking at the entire region
# filtered_taxa_meta_long <- taxa_meta_long
# Calculate mean and standard deviation of species concentration for each date
summary_data <- filtered_taxa_meta_long %>%
group_by(year, month, species) %>%
summarise(mean_concentration = mean(species_concentration, na.rm = TRUE),
sd_concentration = sd(species_concentration, na.rm = TRUE),
earliest_day = min(as.numeric(format(date, "%d")), na.rm = TRUE)) %>%
ungroup()
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
# Combine year and month columns into a single date column
summary_data$date <- as.Date(paste(summary_data$year, summary_data$month, summary_data$earliest_day, sep = "-"))
# custom_pal_hex2 <- c('#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33','#a65628','#f781bf')
custom_pal_hex2 <- c('darkturquoise','red','#4daf4a','slategray4','orange','dodgerblue','purple', "yellow","pink","violet")
# Acantharea = darkturquoise
# Copepods = red
# echinos = #4daf4a
# Jellies = slategray4
# Larvaceans = orange
# Polychaetes = dodgerblue
# Pteropods = purple
# # Check dominance of groups by calculating the overall mean
# test <- summary_data %>% filter(species == "Polychaetes") %>% summarize(avg = mean(mean_concentration))
# mean(test$avg)
# Plot time series of mean+sd planton concentration per group of sites
zz <- ggplot(summary_data, aes(x = date, y = mean_concentration, fill = species)) +
geom_bar(stat = "identity", alpha = 0.7) +
scale_fill_manual(values = custom_pal_hex2) +
# geom_errorbar(aes(ymin = 0, ymax = mean_concentration + sd_concentration),
# position = position_dodge(width = 0.9), width = 0.2) +
labs(x = "Date", y = "Mean Species Concentration", fill = "Species") +
theme_minimal()
zz

# Filter summary_data for selected only
# Acantharea, Copepods, Echinoderms, Jellies, Larvaceans, Chaetognaths, Polychaetes, Pteropods
# Centric,Ceratium,Chaetoceros,Chain2,Chain3,Guinardia,Neocalyptrella,Noctiluca,Trichodesmium
selected_group <- "Chain3"
summary_selected <- filtered_taxa_meta_long %>%
filter(species == selected_group) %>%
filter(species_concentration > 0) %>%
group_by(year, month) %>%
mutate(earliest_day = min(as.numeric(format(date, "%d")), na.rm = TRUE)) %>%
ungroup() %>%
mutate(date = as.Date(paste(year, month, earliest_day, sep = "-"))) %>%
select(-earliest_day) # Optionally, remove the 'earliest_day' column if not needed
# Create the time series boxplot
sel_box <- ggplot(summary_selected, aes(x = as.factor(date), y = species_concentration)) +
geom_boxplot(fill = "white", notch = FALSE, outlier.shape = NA) +
# geom_violin(fill = "lightgrey", color = "NA", alpha = 0.7) +
# geom_jitter(aes(color = factor(Station), size = temp..degC.), alpha = 0.9) +
# geom_jitter(width = 0.25, aes(color = temp..degC., size = salinity), alpha = 0.6) + # with salinity included
geom_jitter(width = 0.25, aes(color = temp..degC.), size = 3, alpha = 0.6) +
labs(x = "Date", y = expression("Density (org. m"^"-3"~")")) +
scale_color_viridis_c(name = "Temperature (°C)", option = "inferno") +
# scale_size_continuous(name = "Salinity", # with salinity included
# limits = c(30, 38),
# breaks = c(30, 32, 34, 36, 38),
# range = c(1, 4)) +
# ylim(0, 30000) +
theme_minimal() +
theme(axis.text.x = element_text(size = 18, color = "black"), # Set X-axis label font size
axis.text.y = element_text(size = 18, color = "black")) +
theme(axis.title.x = element_text(size = 18, color = "black"),
axis.title.y = element_text(size = 18, color = "black")) +
theme(legend.text = element_text(size = 14)) +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
theme(axis.line = element_line(color = "black"))
sel_box

# Save figure as a svg file
# ggsave(paste0("conc_time-series_", selected_group, ".svg"), plot = sel_box, width = 10, height = 8, device = "svg")
# # Plot concentration time series for all selected sites
# kk <- ggplot(summary_selected, aes(x = date, y = species_concentration, shape = factor(Station))) +
# labs(x = "Date", y = "Species concentration (org.m-3)") +
# geom_point(aes(size = temp..degC.), fill = "darkgrey") + # or colour = factor(Station)
# scale_shape_manual(values = c(0, 1, 2, 3, 4, 5, 6))
# kk
# kk <- ggplot(summary_selected, aes(x = date, y = species_concentration, colour = factor(Station))) +
# labs(x = "Date", y = "Species concentration (org.m-3)") +
# geom_point(aes(size = temp..degC.), alpha = 0.7) + # or colour = factor(Station) +
# # geom_smooth(method = "lm", formula = y ~ poly(x, 4), se = FALSE) + # Add polynomial fit line
# scale_colour_brewer(palette = "Set1")
# kk
# # Find the index of the last row with date == '2023-01-15' and add a dummy row for missing dates
# new_row <- tibble(Station = NA, dec_lat = NA, dec_lon = NA, year = NA,
# month = NA, date = as.Date('2023-03-15'), Avg.chl.a..ug.L. = NA,
# temp..degC. = NA, salinity = NA, species = NA, species_concentration = NA)
# last_index <- max(which(summary_selected$date == as.Date('2023-01-15')))
# summary_selected <- bind_rows(
# summary_selected[1:last_index, ],
# new_row,
# summary_selected[(last_index + 1):nrow(summary_selected), ]
# )
# Calculates total plankton concentrations aggregating data from
selected stations (eg FK)
# Calculate plankton concentration for all species summed up
# For Phyto
taxa_meta_concentration_all <- taxa_meta %>%
mutate(total_concentration = (
Centric + Ceratium + Chaetoceros + Chain2 + Chain3 + Guinardia + Neocalyptrella +
Noctiluca + Tricho) / total_vol_sampled * 1e6) %>%
select(Station, dec_lat, dec_lon, year, month, date, Avg.chl.a..ug.L.,
temp..degC., salinity, total_vol_sampled, total_concentration) %>%
filter(!is.na(total_vol_sampled))
# # For Zooplankton
# taxa_meta_concentration_all <- taxa_meta %>%
# mutate(total_concentration = (
# Acantharea + Chaetognaths + Ostracods + Copepods + Decapods + Echinoderms + Jellies +
# Larvaceans + Polychaets + Pteropods) / total_vol_sampled * 1e6) %>%
# select(Station, dec_lat, dec_lon, year, month, date, Avg.chl.a..ug.L.,
# temp..degC., salinity, total_vol_sampled, total_concentration) %>%
# filter(!is.na(total_vol_sampled))
# Filter the data for Stations
station_list_fk <- c("WS","21/LK","MR","16","18","10","12","9.5","9","7","2")
filtered_taxa_conc_all <- taxa_meta_concentration_all %>%
filter(Station %in% station_list_fk)
# Aggregate total concentration and total volume sampled per year and month
aggregated_concentration <- filtered_taxa_conc_all %>%
group_by(year, month) %>%
summarise(
total_counts = sum(total_concentration * total_vol_sampled / 1e6, na.rm = TRUE), # Sum up all counts
total_vol_sampled = sum(total_vol_sampled, na.rm = TRUE), # Sum up all volume sampled
mean_temp_degC = mean(temp..degC., na.rm = TRUE), # Calculate mean temperature
mean_salinity = mean(salinity, na.rm = TRUE), # Calculate mean salinity
mean_chla = mean(Avg.chl.a..ug.L., na.rm = TRUE)
) %>%
ungroup() %>%
mutate(total_concentration = (total_counts / total_vol_sampled) * 1e6) # Recalculate the concentration
`summarise()` has grouped output by 'year'. You can override using the `.groups` argument.
# Create a Date column for plotting purposes
aggregated_concentration <- aggregated_concentration %>%
mutate(year_month = as.Date(paste(year, month, "01", sep = "-")))
# Create the time series plot
concentration_ts <- ggplot(aggregated_concentration, aes(x = year_month)) +
geom_line(aes(y = total_concentration), color = "blue", size = 1) + # Line plot for total concentration
geom_point(aes(y = total_concentration), color = "red", size = 2) + # Points for total concentration
# geom_line(aes(y = (mean_temp_degC - 20) / (35 - 20) * max(total_concentration)),
# color = "orange", size = 1) + # Line plot for temperature (normalized)
labs(title = "Total Phytoplankton Concentration Time Series",
x = "Date",
y = expression("Total Concentration (org/m"^3~")")) +
scale_x_date(date_breaks = "1 month", date_labels = "%b %Y") + # Show ticks for every month
scale_y_continuous(
name = expression("Total Concentration (org/m"^3~")"), # Primary y-axis label
# sec.axis = sec_axis(~ . * (35 - 20) / max(aggregated_concentration$total_concentration) + 20,
# name = "Temperature (°C)",
# breaks = seq(20, 35, by = 5)) # Secondary y-axis label
) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
# axis.title.y.right = element_text(color = "orange"), # Color the secondary y-axis label
# axis.line.y.right = element_line(color = "orange"), # Color the secondary y-axis line
# panel.grid.major = element_blank(), # Remove major grid lines
panel.grid.minor = element_blank(), # Remove minor grid lines
# panel.border = element_blank() # Remove panel border if necessary
)
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
# Print the plot
concentration_ts

# Calculate plankton concentrations per seascape class
spp_df <- taxa_meta[ , c("X8.day.seascapes", "total_vol_sampled", "date",
"Acantharea",
"Copepods",
"Echinoderms",
"Jellies",
"Larvaceans",
"Polychaets",
"Chaetognaths",
"Pteropods")]
# spp_df <- taxa_meta[ , c("X8.day.seascapes", "total_vol_sampled", "date",
# "Ceratium",
# "Chaetoceros",
# "Chain2",
# "Chain3",
# "Guinardia",
# "Neocalyptrella",
# "Tricho")]
# Remove rows with NaN values in X8.day.seascapes and total_vol_sampled
spp_df <- subset(spp_df, !is.na(X8.day.seascapes) & !is.na(total_vol_sampled))
# Calculate total counts per species within each X8.day.seascapes category
spp_count_per_seascape <- spp_df %>%
gather(key = "species", value = "count", -(X8.day.seascapes:date)) %>%
group_by(X8.day.seascapes, species) %>%
summarise(total_count = sum(count)) %>%
ungroup()
`summarise()` has grouped output by 'X8.day.seascapes'. You can override using the `.groups` argument.
# Calculate total volume sampled per X8.day.seascapes category
total_vol_per_seascape <- spp_df %>%
group_by(X8.day.seascapes) %>%
summarise(total_vol_sampled = sum(total_vol_sampled))
# Merge total counts and total volume data frames
spp_concentration <- merge(spp_count_per_seascape, total_vol_per_seascape, by = "X8.day.seascapes")
# Calculate species per cubic meter: might be misleading since it integrates all counts and sampled volumes across cruises to calculate concentrations. It is likely better to use average concentrations as below.
spp_concentration$species_per_cubic_meter <- spp_concentration$total_count / spp_concentration$total_vol_sampled * 1e6
# # Select desired seascapes and reorder categories in X axis
spp_concentration$X8.day.seascapes <- factor(spp_concentration$X8.day.seascapes, levels = c("3", "5", "7", "11", "13", "15","21","27"))
# Filter out seascape class as desired
# exclude_classes <- c(5, 7, 11)
# spp_concentration <- spp_concentration[!spp_concentration$X8.day.seascapes %in% exclude_classes, ]
custom_pal_hex2 <- c('#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33','#a65628','#f781bf')
# Create the stack plot
concentration_stackplot <- ggplot(spp_concentration, aes(x = X8.day.seascapes, y = species_per_cubic_meter, fill = species)) +
geom_bar(stat = "identity") +
scale_fill_manual(values = custom_pal_hex2) +
labs(x = "Seascape class", y = "Species concentration (org. m"^"-3"~")") +
theme_minimal() +
theme(axis.text.x = element_text(size = 18), # Set X-axis label font size
axis.text.y = element_text(size = 18)) +
theme(axis.title.x = element_text(size = 18),
axis.title.y = element_text(size = 18)) +
theme(legend.text = element_text(size = 16))
concentration_stackplot

# Calculate percentages
spp_concentration_percent <- spp_concentration %>%
group_by(X8.day.seascapes) %>%
mutate(total_concentration = sum(species_per_cubic_meter),
species_percentage = (species_per_cubic_meter / total_concentration) * 100)
# Create the stacked plot
concentration_percent_stackplot <- ggplot(spp_concentration_percent, aes(x = X8.day.seascapes, y = species_percentage, fill = species)) +
geom_bar(stat = "identity") +
scale_fill_manual(values = custom_pal_hex2) +
labs(x = "Seascape class", y = "Relative concentration (%)") +
theme_minimal() +
theme(axis.text.x = element_text(size = 18), # Set X-axis label font size
axis.text.y = element_text(size = 18)) +
theme(axis.title.x = element_text(size = 18),
axis.title.y = element_text(size = 18)) +
theme(legend.text = element_text(size = 16))
concentration_percent_stackplot

# Gather columns containing species counts into key-value pairs and calculate concentrations
spp_concentration_long <- spp_df %>%
gather(key = "species", value = "count", -(X8.day.seascapes:date)) %>%
mutate(concentration = (count / total_vol_sampled) * 1e6)
# Calculate the mean concentration and its standard deviation per species and 8X.day.seascapes category. This is likely more representative since the approach will capture high concentration events (high counts in low volumes) that otherwise get filtered out when using an overall integrated sampled volume and total counts for the total concentration.
avg_concentration_per_class <- spp_concentration_long %>%
group_by(species, `X8.day.seascapes`) %>%
summarize(
mean_concentration = mean(concentration, na.rm = TRUE),
sd_concentration = sd(concentration, na.rm = TRUE)
)
`summarise()` has grouped output by 'species'. You can override using the `.groups` argument.
avg_concentration_per_class$X8.day.seascapes <- factor(avg_concentration_per_class$X8.day.seascapes, levels = c("3", "5", "7", "11", "13", "15","21","27"))
avg_stackplot <- ggplot(avg_concentration_per_class, aes(x = X8.day.seascapes, y = mean_concentration, fill = species)) +
geom_bar(stat = "identity") +
# scale_fill_manual(values = custom_pal_hex2) + # use for zooplankton
scale_colour_brewer(palette = "Set1") + # use for phytoplankton
labs(x = "Seascape class", y = "Mean density (org. m"^"-3"~")") +
theme_minimal() +
theme(axis.text.x = element_text(size = 18), # Set X-axis label font size
axis.text.y = element_text(size = 18)) +
theme(axis.title.x = element_text(size = 18),
axis.title.y = element_text(size = 18)) +
theme(legend.text = element_text(size = 16))
avg_stackplot

# Calculate percentages
avg_concentration_percent <- avg_concentration_per_class %>%
group_by(`X8.day.seascapes`) %>%
mutate(mean_concentration_percent = mean_concentration / sum(mean_concentration) * 100)
avg_percent_stackplot <- ggplot(avg_concentration_percent, aes(x = X8.day.seascapes, y = mean_concentration_percent, fill = species)) +
geom_bar(stat = "identity") +
scale_fill_manual(values = custom_pal_hex2) + # use for zooplankton
# scale_colour_brewer(palette = "Set2") + # use for phytoplankton
labs(x = "Seascape class", y = "Relative concentration (%)") +
theme_minimal() +
theme(axis.text.x = element_text(size = 18), # Set X-axis label font size
axis.text.y = element_text(size = 18)) +
theme(axis.title.x = element_text(size = 18),
axis.title.y = element_text(size = 18)) +
theme(legend.text = element_text(size = 16))
avg_percent_stackplot

Generate plots
library(tidyverse)
library(hrbrthemes)
NOTE: Either Arial Narrow or Roboto Condensed fonts are required to use these themes.
Please use hrbrthemes::import_roboto_condensed() to install Roboto Condensed and
if Arial Narrow is not on your system, please see https://bit.ly/arialnarrow
library(viridis)
Loading required package: viridisLite
# # Load seascape color palette used with Matlab and extract RGB values for observed unique seascapes
# For NOAA machines
# palette_dir <- "/Users/enrique.montes/Library/CloudStorage/GoogleDrive-enriquemontes01@gmail.com/My Drive/GDrive/software/matlab/m_map/seascape_cm"
# For personal machine
palette_dir <- "~/Library/CloudStorage/GoogleDrive-enriquemontes01@gmail.com/My Drive/GDrive/software/matlab/m_map/seascape_cm"
palette_file <- list.files(path = palette_dir, pattern = "cmap1.csv", full.names = TRUE)
palette_df <- read.csv(palette_file, header = FALSE)
colnames(palette_df) <- c("r", "g", "b")
unique_seascapes <- sort(unique(na.omit(ctd_meta$X8.day.seascapes)))
subset_palette_df <- palette_df[unique_seascapes, ]
# set RGB values for the plots
r_vals <- round(subset_palette_df$r * 255, 0)
g_vals <- round(subset_palette_df$g * 255, 0)
b_vals <- round(subset_palette_df$b * 255, 0)
custom_pal <- cbind(r_vals, g_vals, b_vals)
custom_pal_hex <- rgb(custom_pal[, 1], custom_pal[, 2], custom_pal[, 3], maxColorValue=255)
# pal_final <- c(custom_pal_hex[3], custom_pal_hex[4], custom_pal_hex[5], )
# filter out rows with NA values in column seascapes
df_filtered <- taxa_meta[complete.cases(taxa_meta$X8.day.seascapes),]
# Convert the 'x' column to character
df_filtered$X8.day.seascapes <- as.character(df_filtered$X8.day.seascapes)
# # Reorder seascape categories in X axis
df_filtered$X8.day.seascapes <- factor(df_filtered$X8.day.seascapes, levels = c("3", "5", "7", "11", "13", "15","21","27"))
# Define custom colors for each level
custom_colors <- c("3" = custom_pal_hex[1], "5" = custom_pal_hex[2], "7" = custom_pal_hex[3],
"11" = custom_pal_hex[4], "13" = custom_pal_hex[5], "15" = custom_pal_hex[6],
"21" = custom_pal_hex[7], "27" = custom_pal_hex[8])
# Filter out seascape class as desired
# df_filtered <- df_filtered[df_filtered$X8.day.seascapes != "5", ]
# Plot
# See https://cran.r-project.org/web/packages/viridis/vignettes/intro-to-viridis.html for color palette options
pp <- df_filtered %>%
ggplot( aes(x=X8.day.seascapes, y=Avg.chl.a..ug.L., fill=X8.day.seascapes)) +
geom_boxplot() +
# scale_fill_viridis(option="H", discrete = TRUE, alpha=0.6) +
scale_fill_manual(values = custom_colors) +
geom_jitter(color="grey", size=0.8, alpha=0.4) +
labs(x = "Seascape class") +
labs(y = expression("["*Chl-a*"]"~ mu*"g"~L^-1)) +
# labs(y = expression("Salinity")) +
# labs(y = expression(paste("Temperature (", degree, "C) at 1 m depth"))) +
# labs(y = expression("["*DO*"]"~mg~L^-1)) +
# labs(y = expression("["*NO["x"]*"]" ~ mu*"M")) +
# labs(y = expression("["*PO[4]^"3-"*"]" ~ mu*"M")) +
# labs(y = expression("["*NH[4]^"+"*"]" ~ mu*"M")) +
# theme(axis.title.y = element_text(hjust = 1))
# scale_x_discrete(labels= c("Tropical/Subtropical Upwelling",
# "Tropical Seas",
# "Warm, Blooms, High Nuts",
# "Tropical/Subtropical Transition",
# "Temperate Transition")) +
theme_ipsum() +
theme(
legend.position="none",
plot.title = element_text(size=11)
) +
# ggtitle("A boxplot with jitter") +
# theme(axis.text.x = element_text(angle = 45)) +
ylim(0, 7) +
theme(axis.text.x = element_text(size = 32, family = "Arial"), # Set X-axis label font size
axis.text.y = element_text(size = 32, family = "Arial")) +
theme(axis.title.x = element_text(size = 18, hjust = 0.5, family = "Arial"),
axis.title.y = element_text(size = 18, hjust = 0.5, family = "Arial")) +
theme(legend.text = element_text(size = 32))
pp
Warning: Removed 184 rows containing non-finite values (`stat_boxplot()`).
Warning: Removed 184 rows containing missing values (`geom_point()`).

Create stackplots showing relative frequency using counts only of
plankton taxa per seascape category
# convert abundance columns to relative frequency
# subsets taxa for zooplankton
# df_subset <- df_filtered[ , c("X8.day.seascapes",
# "Acantharea",
# "Copepods",
# "Echinoderms",
# "Jellies",
# "Larvaceans",
# "Polychaetes",
# "Chaetognaths",
# "Pteropods")]
# subsets taxa for phytoplankton
df_subset <- df_filtered[ , c("X8.day.seascapes",
"Ceratium",
"Chaetoceros",
"Chain2",
"Chain3",
"Guinardia",
"Neocalyptrella",
"Tricho")]
# subsets taxa for all species
# df_subset <- df_filtered[ , c("X8.day.seascapes",
# "Ceratium",
# "Chaetoceros",
# "Diatoms",
# "Diatoms2",
# "Guinardia",
# "Neocalyptrella",
# "Trichodesmium",
# "Acantharea",
# "Copepods",
# "Echinoderms",
# "Jellies",
# "Larvaceans",
# "Polychaetes",
# "Chaetognaths",
# "Pteropods")]
# reshape the data to long format
df_long <- tidyr::gather(df_subset, key = "Species", value = "Frequency", -X8.day.seascapes)
# calculate relative frequencies
df_sum <- df_long %>%
group_by(X8.day.seascapes, Species) %>%
summarise(n = sum(Frequency)) %>%
mutate(freq = n / sum(n))
`summarise()` has grouped output by 'X8.day.seascapes'. You can override using the `.groups` argument.
# Seascape class names:
# "Class 11" - Tropical/Subtropical Upwelling
# "Class 15" - Tropical Seas
# "Class 21" - Warm, Blooms, High Nuts
# "Class 3" - Tropical/Subtropical Transition
# "Class 7" - Temperate Transition
# # Select desired seascapes and reorder categories in X axis
df_sum$X8.day.seascapes <- factor(df_sum$X8.day.seascapes, levels = c("3", "5", "7", "11", "13", "15","21","27"))
# Filter out seascape class as desired
exclude_classes <- c(5, 7, 11)
df_sum <- df_sum[!df_sum$X8.day.seascapes %in% exclude_classes, ]
# Use qualitative palettes: https://colorbrewer2.org/#type=qualitative&scheme=Accent&n=7
custom_pal_hex2 <- c('#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33','#a65628','#f781bf')
# custom_pal_hex2 <- c('#1b9e77','#d95f02','#7570b3','#e7298a','#66a61e','#e6ab02','#a6761d')
# create the stackplot
qq <- ggplot(df_sum, aes(x = X8.day.seascapes, y = freq, fill = Species)) +
# scale_fill_viridis(option="Set3", discrete = TRUE, alpha=0.8) +
scale_fill_viridis(discrete = TRUE, alpha=0.8) +
# scale_fill_manual(values = custom_pal_hex2) +
geom_bar(stat = "identity") +
labs(x = "Seascape class") +
labs(y = "Relative frequency") +
# scale_x_discrete(labels= c("Tropical/Subtropical Upwelling",
# "Tropical Seas",
# "Warm, Blooms, High Nuts",
# "Tropical/Subtropical Transition",
# "Temperate Transition")) +
# theme(axis.text.x = element_text(angle = 45)) +
theme_minimal() +
theme(axis.text.x = element_text(size = 18), # Set X-axis label font size
axis.text.y = element_text(size = 18)) +
theme(axis.title.x = element_text(size = 18),
axis.title.y = element_text(size = 18)) +
theme(legend.text = element_text(size = 16))
#theme(axis.text.x = element_text(hjust = 1))
qq

Compute Shannon Index
library(dplyr)
library(vegan)
# Select species for Shannon calculation
df_subset_shannon <- df_filtered[ , c("X8.day.seascapes",
"Acantharea",
"Copepods",
"Echinoderms",
"Jellies",
"Larvaceans",
"Polychaets",
"Chaetognaths",
"Pteropods",
"Ceratium",
"Chaetoceros",
"Chain2",
"Chain3",
"Guinardia",
"Neocalyptrella",
"Tricho")]
# reshape the data to long format
df_long <- tidyr::gather(df_subset_shannon, key = "Species", value = "Abundance", -X8.day.seascapes)
# Exclude seascapes
exclude_seascapes <- c(5, 7, 11)
# Compute Shannon diversity per seascape class
shannon_df <- df_long %>%
group_by(X8.day.seascapes) %>%
summarise(shannon = diversity(Abundance, index = "shannon"))
# Filter the data to exclude specified seascapes
shannon_df_filtered <- shannon_df[!shannon_df$X8.day.seascapes %in% exclude_seascapes, ]
# create the bar plot of Shannon diversity
ff <- ggplot(shannon_df_filtered, aes(x = X8.day.seascapes, y = shannon, fill = X8.day.seascapes)) +
geom_bar(stat = "identity") +
# scale_fill_viridis(option="plasma", discrete = TRUE, alpha=0.6) +
scale_fill_manual(values = custom_colors) +
xlab("Seascape Class") +
ylab("Shannon Diversity") +
# scale_x_discrete(labels= c("Tropical/Subtropical Upwelling",
# "Tropical Seas",
# "Warm, Blooms, High Nuts",
# "Tropical/Subtropical Transition",
# "Temperate Transition")) +
# theme(axis.text.x = element_text(angle = 45)) +
theme(axis.text.x = element_text(size = 32), # Set X-axis label font size
axis.text.y = element_text(size = 32)) +
theme(axis.title.x = element_text(size = 32),
axis.title.y = element_text(size = 32)) +
theme(legend.text = element_text(size = 32)) +
guides()
ff

# # Subset the data to exclude specified seascapes
filtered_taxa_meta <- taxa_meta[complete.cases(taxa_meta$X8.day.seascapes), ]
# filtered_taxa_meta <- filtered_taxa_meta[!filtered_taxa_meta$X8.day.seascapes %in% exclude_seascapes, ]
# # Plot sampled seascape frequency
tt <- ggplot(filtered_taxa_meta, aes(x = factor(X8.day.seascapes), fill = factor(X8.day.seascapes))) +
geom_bar(color = "black", alpha = 0.7) +
scale_fill_manual(values = custom_colors) +
labs(x = "Seascape Class", y = "Frequency") +
theme_minimal() +
theme(axis.text.x = element_text(size = 22), # Set X-axis label font size
axis.text.y = element_text(size = 22)) +
theme(axis.title.x = element_text(size = 22),
axis.title.y = element_text(size = 22)) +
theme(legend.text = element_text(size = 22)) +
guides(fill = FALSE)
tt

# Calculate percent frequency of each X8.day.seascapes category
# see seascapes classes here: https://coastwatch.noaa.gov/cwn/products/seascape-pelagic-habitat-classification.html
percent_frequency <- filtered_taxa_meta %>%
group_by(X8.day.seascapes) %>%
summarise(count = n()) %>%
mutate(percent_frequency = (count / sum(count)) * 100)
# Display the result
# print(percent_frequency)
# # Counts of species per seascape
df_sum_abund <- df_long %>%
group_by(X8.day.seascapes, Species) %>%
summarise(n = sum(Abundance))
`summarise()` has grouped output by 'X8.day.seascapes'. You can override using the `.groups` argument.
# Display the result
# print(df_sum_abund)
df_sum_abund_filt <- df_sum_abund[df_sum_abund$Species == "Larvaceans",]
# Filter the data to exclude specified seascapes
df_sum_abund_filt2 <- df_sum_abund_filt[!df_sum_abund_filt$X8.day.seascapes %in% exclude_seascapes, ]
dd <- ggplot(df_sum_abund_filt2, aes(x = factor(X8.day.seascapes), y = n, fill = factor(X8.day.seascapes))) +
geom_bar(color = "black", alpha = 0.7, stat = "identity") +
# scale_fill_viridis(option = "magma", discrete = TRUE, alpha = 0.8)
scale_fill_manual(values = custom_colors) +
theme_minimal()
dd

# Compute counts of selected species per seascape class normalized by frequency of seascapes
# reshape the data to long format
df_long2 <- tidyr::gather(df_subset_shannon, key = "Species", value = "Abundance", -X8.day.seascapes)
spp_seascape_normalized <- df_long2 %>%
filter(Species == "Larvaceans") %>%
group_by(X8.day.seascapes) %>%
summarise(spp_count = sum(Abundance))
# Compute frequencies of each seascape class
seascape_frequencies <- df_long2 %>%
count(X8.day.seascapes) %>%
rename(freq = n)
# Merge counts with frequencies
spp_seascape_normalized <- merge(spp_seascape_normalized, seascape_frequencies, by = "X8.day.seascapes")
# Normalize counts by frequency
spp_seascape_normalized$spp_normalized <- (spp_seascape_normalized$spp_count / spp_seascape_normalized$freq) * 1000
spp_seascape_normalized_filt <- spp_seascape_normalized[!spp_seascape_normalized$X8.day.seascapes %in% exclude_seascapes, ]
bb <- ggplot(spp_seascape_normalized_filt, aes(x = factor(X8.day.seascapes), y = spp_normalized, fill = factor(X8.day.seascapes))) +
geom_bar(color = "black", alpha = 0.7, stat = "identity") +
scale_fill_manual(values = custom_colors) +
labs(x = "Seascape Class", y = "Counts / Seascape freq * 1000") +
theme_minimal() +
# theme(axis.text.x = element_text(size = 32), # Set X-axis label font size
# axis.text.y = element_text(size = 32)) +
# theme(axis.title.x = element_text(size = 32),
# axis.title.y = element_text(size = 32)) +
# theme(legend.text = element_text(size = 32)) +
guides()
bb

PC plot
library(ggalt)
Registered S3 methods overwritten by 'ggalt':
method from
grid.draw.absoluteGrob ggplot2
grobHeight.absoluteGrob ggplot2
grobWidth.absoluteGrob ggplot2
grobX.absoluteGrob ggplot2
grobY.absoluteGrob ggplot2
# Perform principal component analysis on the count data
# for hydrography
sel_vars <- c(
"X8.day.seascapes",
"salinity",
"Avg.chl.a..ug.L.",
"PO4...uM.",
"NO3.NO2..uM.",
"NH4...uM.",
"temp..degC.")
# for taxonomy
# sel_vars <- c("X8.day.seascapes",
# "Acantharea",
# "Copepods",
# "Echinoderms",
# "Jellies",
# "Larvaceans",
# "Polychaets",
# "Chaetognaths",
# "Pteropods")
# sel_vars <- c("X8.day.seascapes",
# "Ceratium",
# "Chaetoceros",
# "Chain2",
# "Chain3",
# "Guinardia",
# "Neocalyptrella",
# "Tricho")
# sel_vars <- c("X8.day.seascapes",
# "Acantharea",
# "Copepods",
# "Echinoderms",
# "Jellies",
# "Larvaceans",
# "Polychaets",
# "Chaetognaths",
# "Pteropods",
# "Ceratium",
# "Chaetoceros",
# "Chain2",
# "Chain3",
# "Guinardia",
# "Neocalyptrella",
# "Tricho")
# filter out rows with NA values in column seascapes
# df_filtered_pca <- taxa_meta[complete.cases(taxa_meta$X8.day.seascapes), ]
df_filtered_pca <- taxa_meta[complete.cases(taxa_meta[, sel_vars]), ]
# exclude_seascapes <- c(5, 7, 11)
exclude_seascapes <- 0
filt_df_pca <- df_filtered_pca[!df_filtered_pca$X8.day.seascapes %in%
exclude_seascapes, sel_vars]
# Select numeric columns in filt_df_pca
numeric_cols <- sapply(filt_df_pca, is.numeric)
# Identify numeric columns except the first one
numeric_cols <- 2:ncol(filt_df_pca)
# Transform numeric columns to log scale
filt_df_pca[numeric_cols] <- lapply(filt_df_pca[numeric_cols], function(x) log(x + 1))
# pca <- prcomp(filt_df_pca[, c("salinity", "Avg.chl.a..ug.L.", "PO4...uM.", "NO3.NO2..uM.")], scale. = TRUE)
pca <- prcomp(filt_df_pca[, -1], scale. = TRUE)
# Extract PC1 and PC2 scores for each sampling event
# pc_scores <- data.frame(seascape = df_subset$X8.day.seascapes, # for taxonomic analysis
pc_scores <- data.frame(seascape = as.character(filt_df_pca$X8.day.seascapes), # for hydrography
PC1 = pca$x[, 1],
PC2 = pca$x[, 2])
# Create the plot
pc_scores$seascape <- factor(pc_scores$seascape, levels = c("3", "5", "7", "11", "13", "15", "21", "27"))
bb <- ggplot(pc_scores, aes(x = PC1, y = PC2, color = seascape)) +
geom_point() +
labs(x = "PC1", y = "PC2", color = "Seascape")
# add circle around cluster of data points
# custom_colors_pca <- custom_pal_hex[c(1, 5, 6, 7, 8)]
custom_colors_pca <- custom_colors
yy <- ggplot(pc_scores, aes(x = PC1, y = PC2, color = seascape)) +
geom_point() +
stat_ellipse(aes(fill = seascape), level = 0.90, geom = "polygon", alpha = 0.3, color = "black") +
scale_color_manual(values = custom_colors_pca) +
scale_fill_manual(values = custom_colors_pca) +
theme_classic() +
xlim(-2,2) +
ylim(-2,2) +
# xlim(-1,1) +
# ylim(-1,1) +
geom_point(size=2) +
guides(colour = guide_legend(override.aes = list(size=2))) +
theme(axis.text.x = element_text(size = 32), # Set X-axis label font size
axis.text.y = element_text(size = 32)) +
theme(axis.title.x = element_text(size = 32),
axis.title.y = element_text(size = 32)) +
theme(legend.text = element_text(size = 32))
yy
Warning: Removed 102 rows containing non-finite values (`stat_ellipse()`).
Too few points to calculate an ellipse
Too few points to calculate an ellipse
Warning: Removed 102 rows containing missing values (`geom_point()`).
Warning: Removed 102 rows containing missing values (`geom_point()`).

################################################################################################
# # Create PCA with eigenvectors
# Extract principal component scores
pc_scores2 <- pca$x
# Extract eigenvectors
eigenvectors <- pca$rotation
# Calculate the percentage variance explained by each principal component
total_variance <- sum(pca$sdev^2)
pc_var_percent <- round(100 * (pca$sdev^2) / total_variance, 1)
# Convert X8.day.seascapes to a factor
filt_df_pca$X8.day.seascapes <- as.factor(filt_df_pca$X8.day.seascapes)
# # For hydrography
qq <- ggplot(filt_df_pca, aes(x = pc_scores2[,1], y = pc_scores2[,2], color = X8.day.seascapes)) +
geom_point(size = 3, alpha = 0.7) +
scale_color_manual(values = custom_colors_pca) +
geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 1], yend = eigenvectors[2, 1]),
arrow = arrow(length = unit(0.1, "inches")), color = "black") + # Add vector for PC1
geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 2], yend = eigenvectors[2, 2]),
arrow = arrow(length = unit(0.1, "inches")), color = "black") + # Add vector for PC2
geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 3], yend = eigenvectors[2, 3]),
arrow = arrow(length = unit(0.1, "inches")), color = "black") + # Add vector for PC3
geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 4], yend = eigenvectors[2, 4]),
arrow = arrow(length = unit(0.1, "inches")), color = "black") + # Add vector for PC4
geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 5], yend = eigenvectors[2, 5]),
arrow = arrow(length = unit(0.1, "inches")), color = "black") + # Add vector for PC5
geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 6], yend = eigenvectors[2, 6]),
arrow = arrow(length = unit(0.1, "inches")), color = "black") + # Add vector for PC6
geom_text(aes(x = eigenvectors[1, 1], y = eigenvectors[2, 1],
label = paste("PC1 (", pc_var_percent[1], "%: Salinity)", sep = "")),
vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC1
geom_text(aes(x = eigenvectors[1, 2], y = eigenvectors[2, 2],
label = paste("PC2 (", pc_var_percent[2], "%: Chla)", sep = "")),
vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC2
geom_text(aes(x = eigenvectors[1, 3], y = eigenvectors[2, 3],
label = paste("PC3 (", pc_var_percent[3], "%: Phosphate)", sep = "")),
vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC3
geom_text(aes(x = eigenvectors[1, 4], y = eigenvectors[2, 4],
label = paste("PC4 (", pc_var_percent[4], "%: NOx)", sep = "")),
vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC4
geom_text(aes(x = eigenvectors[1, 5], y = eigenvectors[2, 5],
label = paste("PC5 (", pc_var_percent[5], "%: Ammonium)", sep = "")),
vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC5
geom_text(aes(x = eigenvectors[1, 6], y = eigenvectors[2, 6],
label = paste("PC6 (", pc_var_percent[6], "%: Temperature)", sep = "")),
vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC6
labs(x = "PC1", y = "PC2", color = "Seascape class") +
xlim(-1.5, 1.5) +
ylim(-1.5, 1.5) +
guides(colour = guide_legend(override.aes = list(size=5))) +
theme_classic() +
theme(axis.text.x = element_text(size = 18), # Set X-axis label font size
axis.text.y = element_text(size = 18)) +
theme(axis.title.x = element_text(size = 18),
axis.title.y = element_text(size = 18)) +
theme(legend.text = element_text(size = 18)) +
theme(legend.title = element_text(size = 18))
qq
Warning: Removed 196 rows containing missing values (`geom_point()`).

# # # For phytoplankton
# qq2 <- ggplot(filt_df_pca, aes(x = pc_scores2[,1], y = pc_scores2[,3], color = X8.day.seascapes)) +
# geom_point(size = 4) +
# scale_color_manual(values = custom_colors_pca) +
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 1], yend = eigenvectors[2, 1]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC1
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 2], yend = eigenvectors[2, 2]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC2
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 3], yend = eigenvectors[2, 3]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC3
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 4], yend = eigenvectors[2, 4]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC4
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 5], yend = eigenvectors[2, 5]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC5
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 6], yend = eigenvectors[2, 6]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC6
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 7], yend = eigenvectors[2, 7]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC7
# geom_text(aes(x = eigenvectors[1, 1], y = eigenvectors[2, 1],
# label = paste("PC1 (", pc_var_percent[1], "%: Ceratium)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC1
# geom_text(aes(x = eigenvectors[1, 2], y = eigenvectors[2, 2],
# label = paste("PC2 (", pc_var_percent[2], "%: Chaetoceros)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC2
# geom_text(aes(x = eigenvectors[1, 3], y = eigenvectors[2, 3],
# label = paste("PC3 (", pc_var_percent[3], "%: Diatoms)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC3
# geom_text(aes(x = eigenvectors[1, 4], y = eigenvectors[2, 4],
# label = paste("PC4 (", pc_var_percent[4], "%: Diatoms2)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC4
# geom_text(aes(x = eigenvectors[1, 5], y = eigenvectors[2, 5],
# label = paste("PC5 (", pc_var_percent[5], "%: Guinardia)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC5
# geom_text(aes(x = eigenvectors[1, 6], y = eigenvectors[2, 6],
# label = paste("PC6 (", pc_var_percent[6], "%: Neocalyptrella)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC6
# geom_text(aes(x = eigenvectors[1, 7], y = eigenvectors[2, 7],
# label = paste("PC7 (", pc_var_percent[7], "%: Trichodesmium)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC7
# labs(x = "PC1", y = "PC3", color = "X8.day.seascapes") +
# xlim(-1, 1) +
# ylim(-1, 1) +
# guides(colour = guide_legend(override.aes = list(size=2))) +
# theme_classic() +
# theme(axis.text.x = element_text(size = 32), # Set X-axis label font size
# axis.text.y = element_text(size = 32)) +
# theme(axis.title.x = element_text(size = 32),
# axis.title.y = element_text(size = 32)) +
# theme(legend.text = element_text(size = 32))
# qq2
#
#
# # # For zooplankton
# qq3 <- ggplot(filt_df_pca, aes(x = pc_scores2[,1], y = pc_scores2[,2], color = X8.day.seascapes)) +
# geom_point(size = 4) +
# scale_color_manual(values = custom_colors_pca) +
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 1], yend = eigenvectors[2, 1]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC1
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 2], yend = eigenvectors[2, 2]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC2
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 3], yend = eigenvectors[2, 3]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC3
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 4], yend = eigenvectors[2, 4]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC4
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 5], yend = eigenvectors[2, 5]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC5
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 6], yend = eigenvectors[2, 6]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC6
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 7], yend = eigenvectors[2, 7]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC7
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 8], yend = eigenvectors[2, 8]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC8
# geom_text(aes(x = eigenvectors[1, 1], y = eigenvectors[2, 1],
# label = paste("PC1 (", pc_var_percent[1], "%: Acantharea)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC1
# geom_text(aes(x = eigenvectors[1, 2], y = eigenvectors[2, 2],
# label = paste("PC2 (", pc_var_percent[2], "%: Copepods)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC2
# geom_text(aes(x = eigenvectors[1, 3], y = eigenvectors[2, 3],
# label = paste("PC3 (", pc_var_percent[3], "%: Echinoderms)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC3
# geom_text(aes(x = eigenvectors[1, 4], y = eigenvectors[2, 4],
# label = paste("PC4 (", pc_var_percent[4], "%: Jellies)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC4
# geom_text(aes(x = eigenvectors[1, 5], y = eigenvectors[2, 5],
# label = paste("PC5 (", pc_var_percent[5], "%: Larvaceans)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC5
# geom_text(aes(x = eigenvectors[1, 6], y = eigenvectors[2, 6],
# label = paste("PC6 (", pc_var_percent[6], "%: Polychaetes)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC6
# geom_text(aes(x = eigenvectors[1, 7], y = eigenvectors[2, 7],
# label = paste("PC7 (", pc_var_percent[7], "%: Chaetognaths)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC7
# geom_text(aes(x = eigenvectors[1, 8], y = eigenvectors[2, 8],
# label = paste("PC8 (", pc_var_percent[8], "%: Pteropods)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC8
# labs(x = "PC1", y = "PC2", color = "X8.day.seascapes") +
# xlim(-1, 1) +
# ylim(-1, 1) +
# guides(colour = guide_legend(override.aes = list(size=2))) +
# theme_classic() +
# theme(axis.text.x = element_text(size = 32), # Set X-axis label font size
# axis.text.y = element_text(size = 32)) +
# theme(axis.title.x = element_text(size = 32),
# axis.title.y = element_text(size = 32)) +
# theme(legend.text = element_text(size = 32))
# qq3
Map overall WEIGHTED mean concentration values of selected taxa by
transect
# install.packages("svglite")
library(ggOceanMaps)
Warning: package ‘ggOceanMaps’ was built under R version 4.2.3
ggOceanMaps: Setting data download folder to a temporary folder
/var/folders/pj/01bgh26153x4c6rjpm45t1hrt405dy/T//Rtmp1csphH. This means that any downloaded map
data need to be downloaded again when you restart R. To avoid this problem, change the default
path to a permanent folder on your computer. Add following lines to your .Rprofile file:
{.ggOceanMapsenv <- new.env(); .ggOceanMapsenv$datapath <- 'YourCustomPath'}. You can use
usethis::edit_r_profile() to edit the file. '~/ggOceanMapsLargeData' would make it in a writable
folder on most operating systems.
library(leaflet)
Registered S3 method overwritten by 'htmlwidgets':
method from
print.htmlwidget tools:rstudio
# library(patchwork)
# List of class names
# Acantharea, Chaetognaths, Ostracods, Copepods, Decapods, Echinoderms, Jellies, Larvaceans, Polychaets, Pteropods
# Centric, Ceratium, Chaetoceros, Chain2, Chain3, Guinardia, Neocalyptrella, Noctiluca, Tricho
selected_class <- "Tricho"
# For zooplankton
taxa_meta_concentration <- taxa_meta %>%
mutate(across(all_of(selected_class), ~ . / total_vol_sampled * 1e6)) %>%
select(Station, dec_lat, dec_lon, year, month, date, total_vol_sampled, all_of(selected_class)) %>%
filter(!is.na(total_vol_sampled))
# # For phytoplankton
# taxa_meta_concentration <- taxa_meta %>%
# mutate(across(c(Centric,Ceratium,Chaetoceros,Chain2,Chain3,Guinardia,Neocalyptrella,
# Noctiluca,Tricho), ~ ./total_vol_sampled * 1e6)) %>%
# select(Station, dec_lat, dec_lon, year, month, date, Avg.chl.a..ug.L.,
# temp..degC., salinity,total_vol_sampled,
# Centric,Ceratium,Chaetoceros,Chain2,Chain3,Guinardia,Neocalyptrella,
# Noctiluca,Tricho) %>%
# filter(!is.na(total_vol_sampled))
# Get station coordinates
path_sfer_list <- "~/Library/CloudStorage/GoogleDrive-enriquemontes01@gmail.com/My Drive/GDrive/proposals/2022_02_MultiStressor_NOAA/module_1"
sfer_curated <- list.files(path_sfer_list, pattern = "sfer_stations_curated.csv", full.names = TRUE)
sfer_sta_list <- read.csv(sfer_curated, header = TRUE)
# # Merges curated station list with the concentration df
concentration_df <- taxa_meta_concentration %>%
left_join(sfer_sta_list %>% filter(station_class %in% "C"), by = c('Station' = 'station_id')) %>%
filter(get(selected_class) > 0) # Exclude rows where the species count is zero
# Compute weighted average lat/lons and mean variable values for the current class
taxa_concentration_avg <- concentration_df %>%
group_by(line_id) %>%
mutate(weight = get(selected_class) / sum(get(selected_class), na.rm = TRUE)) %>%
summarise(longitude = weighted.mean(dec_lon, w = weight, na.rm = TRUE),
latitude = weighted.mean(dec_lat, w = weight, na.rm = TRUE),
sel_taxa_mean = mean(get(selected_class), na.rm = TRUE),
sel_taxa_sd = sd(get(selected_class), na.rm = TRUE))
# Prepare data for mapping
dt <- data.frame(
lon = taxa_concentration_avg$longitude,
lat = taxa_concentration_avg$latitude,
mean_param = taxa_concentration_avg$sel_taxa_mean,
sd_param = taxa_concentration_avg$sel_taxa_sd) %>%
mutate(sd_param = replace_na(sd_param, 0))
# Add station markers
station_markers <- sfer_sta_list %>% filter(station_class %in% "C")
# Create the map
concentration_map <- basemap(limits = c(-86, -79.5, 24, 28.5), bathymetry = TRUE) +
# geom_point(data = dt, aes(x = lon, y = lat, size = log10(mean_param+1), color = log10(sd_param+1))) +
geom_point(data = dt, aes(x = lon, y = lat, size = mean_param, color = sd_param)) +
scale_color_gradient(low = "yellow", high = "red", na.value = NA, name = "Standard Deviation") +
geom_point(data = station_markers, aes(x = mean_lon, y = mean_lat), color = "black", shape = 3, size = 1.5) +
scale_size_continuous(name = "Average concentration",
# breaks = c(2000, 4000, 6000),
range = c(0, 10),
guide = guide_legend(override.aes = list(color = "black", fill = "white"))) +
theme_minimal() +
ggtitle(paste(selected_class))
# Optionally, save each map as an image
ggsave(paste0("conc_map_weighted_", selected_class, ".png"), plot = concentration_map, width = 8, height = 6)
ggsave(paste0("conc_map_weighted_", selected_class, ".svg"), plot = concentration_map, width = 8, height = 6, device = "svg")
print(concentration_map)

Map overall mean concentration values of selected taxa per
station
# List of class names
# Acantharea, Chaetognaths, Ostracods, Copepods, Decapods, Echinoderms, Jellies, Larvaceans, Polychaets, Pteropods, pellets
# Centric, Ceratium, Chaetoceros, Chain2, Chain3, Guinardia, Neocalyptrella, Noctiluca, Tricho
selected_class <- "Chain3"
taxa_meta_concentration <- taxa_meta %>%
mutate(across(all_of(selected_class), ~ . / total_vol_sampled * 1e6)) %>%
select(Station, dec_lat, dec_lon, year, month, date, total_vol_sampled, all_of(selected_class)) %>%
filter(!is.na(total_vol_sampled))
# Use this to select a specific time period
taxa_meta_concentration <- taxa_meta_concentration %>% filter(year == 2023 & month == 7)
# Compute mean concentration of selected groups per station
taxa_concentration_avg_station <- taxa_meta_concentration %>%
group_by(Station) %>%
summarise(longitude_station = mean(dec_lon, na.rm = TRUE),
latitude_station = mean(dec_lat, na.rm = TRUE),
sel_taxa_mean_station = mean(get(selected_class), na.rm = TRUE),
sel_taxa_sd_station = sd(get(selected_class), na.rm = TRUE))
# Get station coordinates
path_sfer_list <- "~/Library/CloudStorage/GoogleDrive-enriquemontes01@gmail.com/My Drive/GDrive/proposals/2022_02_MultiStressor_NOAA/module_1"
sfer_curated <- list.files(path_sfer_list, pattern = "sfer_stations_curated.csv", full.names = TRUE)
sfer_sta_list <- read.csv(sfer_curated, header = TRUE)
# # Map with bathymetry
# Overlay color-scaled dots
dt_station <- data.frame(
lon_station = taxa_concentration_avg_station$longitude_station,
lat_station = taxa_concentration_avg_station$latitude_station,
mean_param_station = taxa_concentration_avg_station$sel_taxa_mean_station,
sd_param_station = taxa_concentration_avg_station$sel_taxa_sd_station)
# Replace NA by zeros
dt_station <- dt_station %>%
mutate(sd_param_station = replace_na(sd_param_station, 0))
# Add station markers
station_markers <- sfer_sta_list %>% filter(station_class %in% "C")
concentration_map_station <- basemap(limits = c(-86, -79.5, 24, 28.5), bathymetry = TRUE) +
geom_point(data = dt_station, aes(x = lon_station,
y = lat_station,
size = mean_param_station,
color = sd_param_station)) +
scale_color_gradient(low = "yellow", high = "red", na.value = NA, name = "Standard Deviation") +
geom_point(data = station_markers, aes(x = mean_lon, y = mean_lat), color = "black", shape = 3, size = 1.5) +
scale_size_continuous(name = "Average concentration",
# breaks = c(250, 500, 750),
range = c(1, 10),
guide = guide_legend(override.aes = list(color = "black", fill = "white"))) +
theme_minimal()
# Optionally, save each map as an image
# ggsave(paste0("conc_map_station_", selected_class, ".png"), plot = concentration_map_station, width = 8, height = 6)
# ggsave(paste0("conc_map_station_", selected_class, ".svg"), plot = concentration_map_station, width = 8, height = 6, device = "svg")
print(concentration_map_station)
# write.csv(dt_station, paste0("df_",selected_class,".csv"), row.names = FALSE)
print(max(dt_station$mean_param_station))
Map mean count values of selected taxa and specified dates
# # ggOceanMaps:
# https://biostats-r.github.io/biostats/workingInR/140_maps.html
# https://github.com/MikkoVihtakari/ggOceanMaps: Use this one: remotes::install_github("MikkoVihtakari/ggOceanMaps")
# install.packages("ggOceanMaps") # # This is outdated, don't use!!!
library(ggOceanMaps)
library(leaflet)
# For NOAA machines
# path_sfer_list <- "/Users/enrique.montes/Library/CloudStorage/GoogleDrive-enriquemontes01@gmail.com/My Drive/GDrive/proposals/2022_02_MultiStressor_NOAA/module_1"
# For personal machine
path_sfer_list <- "~/Library/CloudStorage/GoogleDrive-enriquemontes01@gmail.com/My Drive/GDrive/proposals/2022_02_MultiStressor_NOAA/module_1"
sfer_curated <- list.files(path_sfer_list, pattern = "sfer_stations_curated.csv", full.names = TRUE)
sfer_sta_list <- read.csv(sfer_curated, header = TRUE)
merged_data <- taxa_meta %>%
left_join(sfer_sta_list %>% filter(station_class %in% "C"), by = c('Station' = 'station_id'))
# Replace 2023 and 10 with your selected year and month
selected_year <- 2023
selected_month <- 11
# Filter rows
filtered_taxa_meta <- merged_data %>%
filter(year == selected_year, month == selected_month)
# filtered_taxa_meta <- merged_data
# Compute weighted average lat lons based on selected variable, and mean variable values (e.g., Copepods)
taxa_avg <- filtered_taxa_meta %>%
group_by(line_id) %>%
mutate(weight = Guinardia / sum(Guinardia, na.rm = TRUE)) %>%
summarise(longitude = weighted.mean(dec_lon, w = weight, na.rm = TRUE),
latitude = weighted.mean(dec_lat, w = weight, na.rm = TRUE),
sel_taxa_mean = mean(Guinardia, na.rm = TRUE))
# # Filter out values less than 1
filtered_taxa_avg <- taxa_avg %>%
filter(sel_taxa_mean > 0)
# Find the minimum and maximum values of sel_taxa_mean
min_value <- min(filtered_taxa_avg$sel_taxa_mean, na.rm = TRUE)
max_value <- max(filtered_taxa_avg$sel_taxa_mean, na.rm = TRUE)
# # Adjust the color palette with specified values
# color_palette <- colorNumeric(palette = "Oranges", domain = c(min_value, max_value))
# # Create the map
# map <- leaflet(filtered_taxa_avg) %>%
# addProviderTiles("Esri.WorldImagery") %>%
# addCircleMarkers(
# lng = ~longitude,
# lat = ~latitude,
# radius = 10,
# color = ~color_palette(sel_taxa_mean),
# fillOpacity = 0.8,
# popup = ~paste("Line ID: ", line_id, "<br>Copepods: ", sel_taxa_mean)
# ) %>%
# addLegend(
# position = "bottomright",
# pal = color_palette,
# values = c(min_value, max_value), # Adjusted values here
# title = "Copepod occurrences",
# opacity = 1
# )
# map
# # Map with bathymetry
# Overlay color-scaled dots
dt <- data.frame(
lon = filtered_taxa_avg$longitude,
lat = filtered_taxa_avg$latitude,
sel_param = filtered_taxa_avg$sel_taxa_mean)
# # Colors:
# Acantharea = darkturquoise; breaks = c(0.25, 1, 2.5, 5); limits = c(0.1, 10)
# Copepods = red; breaks = c(0.25, 1, 2.5, 5, 10); limits = c(0.1, 15)
# Chain diatoms = orange; breaks = c(0.5, 1, 10, 30, 60); c(0.1, 70)
# Chaetoceros = purple; breaks = c(0.25, 1, 2.5, 5, 10); limits = c(0.1, 15)
# Echinoderms = magenta; breaks = c(0.5, 1, 3, 5); limits = c(0.1, 5)
# Larvaceans = plum; breaks = c(0.5, 1, 3, 5); limits = c(0.1, 5)
# Polychaetes = dodgerblue; breaks = c(0.25, 0.5, 1, 3); limits = c(0.1, 3)
# Jellies = slategray4; breaks = c(0.25, 0.5, 1, 2); limits = c(0.1, 3)
occ_map <- basemap(limits = c(-84, -79.5, 24, 28.5), bathymetry = TRUE) +
geom_point(data = dt, aes(x = lon, y = lat, size = sel_param), fill = "olivedrab2", color = "olivedrab2") +
geom_point(data = filtered_taxa_meta, aes(x = dec_lon, y = dec_lat), color = "black", shape = 3, size = 1.5) +
scale_size_continuous(name = "Average counts",
breaks = c(0.5, 1, 3, 5, 10),
limits = c(0.1, 40),
range = c(1, 15)) +
theme_minimal()
occ_map
# occ_map_leg <- ggplot(dt, aes(lon, lat)) +
# geom_point(aes(size = sel_param), fill = "green", color ="green") +
# theme_minimal()
# occ_map_leg
Create time-series plots of selected taxa at specific stations
selected_variable <- 'Chaetoceros'
# # Lower Keys
# selected_line_id <- c('LK','WS','MO','KW')
# # Middle Keys
# selected_line_id <- c('CO', 'CR')
# # Other regions
selected_line_id <- c('CAL')
# Filter the data for the selected line_id
filtered_merged_data <- merged_data[merged_data$line_id %in% selected_line_id, ]
# Remove rows with NAs in relevant columns
monthly_means <- na.omit(filtered_merged_data[, c("date", selected_variable, "line_id")]) %>%
group_by(year_month = format(date, "%Y-%m")) %>%
summarise(mean_value = mean(!!sym(selected_variable), na.rm = TRUE),
se_value = sd(!!sym(selected_variable), na.rm = TRUE) / sqrt(n()))
# Create a time series plot
ggplot(monthly_means, aes(x = year_month, y = mean_value)) +
geom_bar(stat = "identity", fill = "orange") +
geom_errorbar(aes(ymin = mean_value - se_value, ymax = mean_value + se_value),
width = 0.2, # Adjust width as needed
position = position_dodge(width = 0.8)) + # Adjust width as needed
labs(x = "Year-Month", y = "Mean Value") +
ggtitle("Monthly Means of Selected Variable") +
theme_minimal()
Match file name with string ID - DO NOT USE
# library(tidyverse)
# library(lubridate)
# library(dplyr)
#
# # extract relevant part of the strings
# df <- rbind(class.Copepods,
# class.Eucampia,
# class.Noctiluca,
# class.Polychaets,
# class.Acantharea,
# class.Centric,
# class.Ceratium,
# class.Chaetoceros,
# class.Chain2,
# class.Chain3,
# class.Chain4,
# class.Ostracods,
# class.Jellies,
# class.Larvaceans,
# class.pellets)
# sub_strings <- substr(df$V1, start = 10, stop = 22)
# unique_all <- unique(sub_strings)
#
# # select unique dates (this allows to search CTD records per date and time)
# # To find unique dates and times to extract CDT data use: unique_all[grepl("20221209", unique_all)]
#
# id_list <- unique_all
# id_list2 <- as.POSIXct(id_list, format="%Y%m%d_%H%M", tz="UTC")
#
# conc_occ_count <- data.frame(date = as.Date(character()), stringsAsFactors = FALSE)
#
# for ( i in seq_along(id_list)){
# acantha <- as.data.frame(str_count(class.Acantharea$V1, id_list[i]))
# centric <- as.data.frame(str_count(class.Centric$V1, id_list[i]))
# ceratium <- as.data.frame(str_count(class.Ceratium$V1, id_list[i]))
# chaetoceros <- as.data.frame(str_count(class.Chaetoceros$V1, id_list[i]))
# chaetog <- as.data.frame(str_count(class.Chaetognaths$V1, id_list[i]))
# chain2 <- as.data.frame(str_count(class.Chain2$V1, id_list[i]))
# chain3 <- as.data.frame(str_count(class.Chain3$V1, id_list[i]))
# chain4 <- as.data.frame(str_count(class.Chain4$V1, id_list[i]))
# ostra <- as.data.frame(str_count(class.Ostracods$V1, id_list[i]))
# copepods <- as.data.frame(str_count(class.Copepods$V1, id_list[i]))
# decapod <- as.data.frame(str_count(class.Decapods$V1, id_list[i]))
# echino <- as.data.frame(str_count(class.Echinoderms$V1, id_list[i]))
# eucampia <- as.data.frame(str_count(class.Eucampia$V1, id_list[i]))
# jellies <- as.data.frame(str_count(class.Jellies$V1, id_list[i]))
# larvae <- as.data.frame(str_count(class.Larvaceans$V1, id_list[i]))
# nocti <- as.data.frame(str_count(class.Noctiluca$V1, id_list[i]))
# polychaetes <- as.data.frame(str_count(class.Polychaets$V1, id_list[i]))
# tricho <- as.data.frame(str_count(class.Tricho$V1, id_list[i]))
#
# Acantharea <- colSums(acantha != 0)
# Centric <- colSums(centric != 0)
# Ceratium_spp <- colSums(ceratium != 0)
# Chaetoceros <- colSums(chaetoceros != 0)
# Chaetognaths <- colSums(chaetog != 0)
# Diatom_chains_1 <- colSums(chain2 != 0)
# Diatom_chains_2 <- colSums(chain3 != 0)
# Diatom_chains_3 <- colSums(chain4 != 0)
# Ostracods <- colSums(ostra != 0)
# Copepods <- colSums(copepods != 0)
# Decapods <- colSums(decapod != 0)
# Echinoderms <- colSums(echino != 0)
# Eucampia_spp <- colSums(eucampia != 0)
# Jellies<- colSums(jellies != 0)
# Larvaceans <- colSums(larvae != 0)
# Noctiluca <- colSums(nocti != 0)
# Polychaetes <- colSums(polychaetes != 0)
# Trichodesmium_spp <- colSums(tricho != 0)
#
# # Parse the date-time string with ymd_hm()
# occ_datetime <- as.POSIXct(id_list[i], format="%Y%m%d_%H%M", tz="UTC")
# occ_datetime_str <- substr(id_list[i], 1, 13)
#
# row_df <- data.frame(date = occ_datetime, occ_datetime_str,
# Acantharea,
# Centric,
# Ceratium_spp,
# Chaetoceros,
# Chaetognaths,
# Diatom_chains_1,
# Diatom_chains_2,
# Diatom_chains_3,
# Ostracods,
# Copepods,
# Decapods,
# Echinoderms,
# Eucampia_spp,
# Jellies,
# Larvaceans,
# Noctiluca,
# Polychaetes,
# Trichodesmium_spp
# )
# rownames(row_df) <- i
#
# conc_occ_count <- rbind(conc_occ_count, row_df)
# }
#
# conc_occ_final <- arrange(conc_occ_count, date)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vazogU3RyaW5nbWF0Y2giCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCgojIExvYWQgZGF0YSBmaWxlcwpgYGB7cn0KbGlicmFyeShkcGx5cikKCiMgc3BlY2lmeSB0aGUgZGlyZWN0b3J5IHdoZXJlIHRoZSBmaWxlcyBhcmUgbG9jYXRlZAojIEZvciBOT0FBIG1hY2hpbmVzCiMgZGlyX3BhdGggPC0gIi9Vc2Vycy9lbnJpcXVlLm1vbnRlcy9Hb29nbGUgRHJpdmUvTXkgRHJpdmUvR0RyaXZlL09DRURfQU9NTC9XU19jcnVpc2VzL3BsYW5rdG9uX2ltYWdpbmcvQ1BJQ1MvVFMuTWFzdGVyX3NlbGVjdGlvbiIKIyBGb3IgcGVyc29uYWwgbWFjaGluZQpkaXJfcGF0aCA8LSAifi9MaWJyYXJ5L0Nsb3VkU3RvcmFnZS9Hb29nbGVEcml2ZS1lbnJpcXVlbW9udGVzMDFAZ21haWwuY29tL015IERyaXZlL0dEcml2ZS9PQ0VEX0FPTUwvV1NfY3J1aXNlcy9wbGFua3Rvbl9pbWFnaW5nL0NQSUNTL1RTLk1hc3Rlcl9zZWxlY3Rpb24iCgojIG9idGFpbiBhIGxpc3Qgb2YgZmlsZSBuYW1lcyBpbiB0aGUgZGlyZWN0b3J5CmZpbGVfbmFtZXMgPC0gbGlzdC5maWxlcyhwYXRoID0gZGlyX3BhdGgsIHBhdHRlcm4gPSAiLnR4dCIsIGZ1bGwubmFtZXMgPSBUUlVFKQoKIyBsb29wIG92ZXIgZWFjaCBmaWxlIGFuZCBpbXBvcnQgdGhlIHRhYmxlcyAodXNlIHRoaXMgZm9yIERBVEVTKQpmb3IgKGZpbGUgaW4gZmlsZV9uYW1lcykgewogIHRhYmxlX25hbWUgPC0gZ3N1YigiLnR4dCIsICIiLCBiYXNlbmFtZShmaWxlKSkgIyBnZXQgdGhlIG5hbWUgb2YgdGhlIHRhYmxlIGZyb20gdGhlIGZpbGUgbmFtZQogIGFzc2lnbih0YWJsZV9uYW1lLCByZWFkLnRhYmxlKGZpbGUgPSBmaWxlLCBoZWFkZXIgPSBGQUxTRSwgc2VwID0gIlx0IikgJT4lCiAgICAgICAgICAgbXV0YXRlKGRhdGUgPSBhcy5QT1NJWGN0KHN1YnN0cihWMSwgc3RhcnQgPSAyNCwgc3RvcCA9IDM2KSwgZm9ybWF0PSIlWSVtJWRfJUglTSIsIHR6PSJVVEMiKSkpCn0KCiMgIyBsb29wIG92ZXIgZWFjaCBmaWxlIGFuZCBpbXBvcnQgdGhlIHRhYmxlcyAodXNlIHRoaXMgZm9yIFNUUklOR1MpCiMgZm9yIChmaWxlIGluIGZpbGVfbmFtZXMpIHsKIyAgIHRhYmxlX25hbWUgPC0gZ3N1YigiLnR4dCIsICIiLCBiYXNlbmFtZShmaWxlKSkgIyBnZXQgdGhlIG5hbWUgb2YgdGhlIHRhYmxlIGZyb20gdGhlIGZpbGUgbmFtZQojICAgYXNzaWduKHRhYmxlX25hbWUsIHJlYWQudGFibGUoZmlsZSA9IGZpbGUsIGhlYWRlciA9IEZBTFNFLCBzZXAgPSAiXHQiKSkKIyB9CgpgYGAKCgojIE1hdGNoIGZpbGUgbmFtZSB1c2luZyBEQVRFIHZhbHVlcwpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobHVicmlkYXRlKQoKZGlyX3BhdGgyIDwtICJ+L0xpYnJhcnkvQ2xvdWRTdG9yYWdlL0dvb2dsZURyaXZlLWVucmlxdWVtb250ZXMwMUBnbWFpbC5jb20vTXkgRHJpdmUvR0RyaXZlL09DRURfQU9NTC9XU19jcnVpc2VzL3BsYW5rdG9uX2ltYWdpbmcvQ1BJQ1Mvd3NfY3J1aXNlX2N0ZCIKCmZpbGVfbmFtZSA8LSBsaXN0LmZpbGVzKHBhdGggPSBkaXJfcGF0aDIsIHBhdHRlcm4gPSAiY3RkX21ldGFfdjMuY3N2IiwgZnVsbC5uYW1lcyA9IFRSVUUpCmN0ZF9tZXRhIDwtIHJlYWQuY3N2KGZpbGVfbmFtZSwgZmlsbCA9IFRSVUUpCgojIFVTRSBXSVRIIGN0ZF9tZXRhX3YzLmNzdgpkdF9saXN0IDwtIGFzLlBPU0lYY3QocGFzdGUoY3RkX21ldGEkeWVhciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwcmludGYoIiUwMmQiLCBjdGRfbWV0YSRtb250aCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcHJpbnRmKCIlMDJkIiwgY3RkX21ldGEkZGF5KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN0ZF9tZXRhJHRpbWVfZ210KSwKICAgICAgICAgICAgICAgICAgICAgIGZvcm1hdCA9ICIlWSVtJWQgJUk6JU06JVMgJXAiLAogICAgICAgICAgICAgICAgICAgICAgdHogPSAiVVRDIikKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBUaGlzIHNlY3Rpb24gZGV0ZWN0cyBzaG9ydCB0cmFuc2l0IHRpbWVzIGJldHdlZW4gc3RhdGlvbnMKCiMgQ2FsY3VsYXRlIHRpbWUgZGlmZmVyZW5jZXMgaW4gc2Vjb25kcyBiZXR3ZWVuIGNvbnNlY3V0aXZlIGR0X2xpc3Qgb2JqZWN0cwp0aW1lX2RpZmZlcmVuY2VzIDwtIGFzLm51bWVyaWMoZGlmZnRpbWUoZHRfbGlzdFstMV0sIGR0X2xpc3RbLWxlbmd0aChkdF9saXN0KV0sIHVuaXRzID0gInNlY3MiKSkKCiMgQ29udmVydCB0aW1lIGRpZmZlcmVuY2VzIGZyb20gc2Vjb25kcyB0byBtaW51dGVzCnRpbWVfZGlmZmVyZW5jZXNfbWlucyA8LSB0aW1lX2RpZmZlcmVuY2VzIC8gNjAKCiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSBzaG93aW5nIHRoZSBvcmlnaW5hbCB0aW1lcyBhbmQgdGhlaXIgZGlmZmVyZW5jZXMgaW4gbWludXRlcwp0aW1lX2RpZmZfZGYgPC0gZGF0YS5mcmFtZSgKICBzdGFydF90aW1lID0gZHRfbGlzdFstbGVuZ3RoKGR0X2xpc3QpXSwKICBlbmRfdGltZSA9IGR0X2xpc3RbLTFdLAogIHRpbWVfZGlmZmVyZW5jZV9taW5zID0gdGltZV9kaWZmZXJlbmNlc19taW5zCikKCiMgZmluZCBDVEQgdGltZSBzdGFtcHMgb2YgY29uc2VjdXRpdmUgc3RhdGlvbnMgd2l0aGluIGxlc3MgdGhhbiAyMCBtaW4uIFRoaXMgd2lsbCBpZGVudGlmeSBDVEQgY2FzdHMgdGhhdCBhcmUgY2xvc2UgdG8gZWFjaCBvdGhlcgpzaG9ydF90X2lkeCA8LSB3aGljaCh0aW1lX2RpZmZfZGYkdGltZV9kaWZmZXJlbmNlX21pbnMgPCAyMCkKc2hvcnRfdGltZXN0YW1wcyA8LSBkdF9saXN0W3Nob3J0X3RfaWR4XQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyBDcmVhdGUgZW1wdHkgZGF0YSBmcmFtZSB0byBzdG9yZSByZXN1bHRzCmNvbmNfb2NjX2ZpbmFsIDwtIGRhdGEuZnJhbWUoZGF0ZSA9IGNoYXJhY3RlcigpLCBjb3VudCA9IG51bWVyaWMoKSkKCiMgTGlzdCBvZiBjbGFzcyBvYmplY3RzIHRvIGJlIHByb2Nlc3NlZApjbGFzc19uYW1lcyA8LSBjKCJBY2FudGhhcmVhIiwgIkNlbnRyaWMiLCAiQ2VyYXRpdW0iLCAiQ2hhZXRvY2Vyb3MiLCAiQ2hhZXRvZ25hdGhzIiwgCiAgICAgICAgICAgICAgICAgIkNoYWluMiIsICJDaGFpbjMiLCAiT3N0cmFjb2RzIiwgIkNvcGVwb2RzIiwgIkRlY2Fwb2RzIiwgIkVjaGlub2Rlcm1zIiwgCiAgICAgICAgICAgICAgICAgIkd1aW5hcmRpYSIsICJKZWxsaWVzIiwgIkxhcnZhY2VhbnMiLCAiTmVvY2FseXB0cmVsbGEiLCAiTm9jdGlsdWNhIiwgCiAgICAgICAgICAgICAgICAgInBlbGxldHMiLCAiUG9seWNoYWV0cyIsICJQdGVyb3BvZHMiLCAiVHJpY2hvIikKCiMgdGltZSBidWZmZXIgYmVmb3JlIGFuZCBhZnRlciBDVEQgdGltZSBpbiBzZWNvbmRzIHNvIHRoYXQgQ1BJQ1MgcmVjb3JkcyBhcmUgbWF0Y2hlZCB0byBDVEQgdGltZXMuCnN0YXJ0IDwtIDEwICogNjAgCnN0b3AgPC0gMTAgKiA2MCAKCiMgSXRlcmF0ZSBvdmVyIGR0X2xpc3QgaW50ZXJ2YWxzCmZvciAoaSBpbiAxOmxlbmd0aChkdF9saXN0KSkgewogIAogICMgSW5pdGlhbGl6ZSBhIGxpc3QgdG8gc3RvcmUgY291bnRzIGZvciB0aGUgY3VycmVudCBpbnRlcnZhbAogIGNvdW50c19saXN0IDwtIGxpc3QoZGF0ZSA9IGR0X2xpc3RbaV0pCiAgCiAgIyBJdGVyYXRlIG92ZXIgZWFjaCBjbGFzcyBvYmplY3QgYW5kIHBlcmZvcm0gc3Vic2V0dGluZwogIGZvciAoY2xhc3NfbmFtZSBpbiBjbGFzc19uYW1lcykgewogICAgY2xhc3NfZGF0YSA8LSBnZXQocGFzdGUwKCJjbGFzcy4iLCBjbGFzc19uYW1lKSkgICMgRHluYW1pY2FsbHkgZ2V0IHRoZSBjbGFzcyBkYXRhIGZyYW1lCiAgICAKICAgIGlmIChpIDwgbGVuZ3RoKGR0X2xpc3QpKSB7CiAgICAgICMgU3Vic2V0dGluZyBmb3IgYWxsIGludGVydmFscyBleGNlcHQgdGhlIGxhc3Qgb25lCiAgICAgIHN1YnNldF9kYXRhIDwtIHN1YnNldChjbGFzc19kYXRhLCBkYXRlID49IGR0X2xpc3RbaV0tc3RhcnQgJiBkYXRlIDwgZHRfbGlzdFtpKzFdLXN0b3ApCiAgICB9IGVsc2UgewogICAgICAjIFN1YnNldHRpbmcgZm9yIHRoZSBsYXN0IGludGVydmFsOiBjYXB0dXJlIGFsbCBkYXRhIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byB0aGUgbGFzdCBkdF9saXN0CiAgICAgIHN1YnNldF9kYXRhIDwtIHN1YnNldChjbGFzc19kYXRhLCBkYXRlID49IGR0X2xpc3RbaV0tc3RhcnQpCiAgICB9CiAgICAKICAgIGNvdW50c19saXN0W1tjbGFzc19uYW1lXV0gPC0gbnJvdyhzdWJzZXRfZGF0YSkKICB9CiAgCiAgIyBDb252ZXJ0IGNvdW50c19saXN0IHRvIGEgZGF0YSBmcmFtZSBhbmQgYmluZCBpdCB0byB0aGUgcmVzdWx0CiAgcmVzdWx0IDwtIGFzLmRhdGEuZnJhbWUoY291bnRzX2xpc3QpCiAgY29uY19vY2NfZmluYWwgPC0gcmJpbmQoY29uY19vY2NfZmluYWwsIHJlc3VsdCkKfSAKCiMgQ29tYmluZSB3aXRoIGN0ZF9tZXRhCnRheGFfbWV0YSA8LSBjYmluZChjdGRfbWV0YSwgY29uY19vY2NfZmluYWwpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENoZWNrIGZvciB1bmFjY291bnRlZCBDUElDUyByZWNvcmRzIAoKIyBJbml0aWFsaXplIGEgbGlzdCB0byBzdG9yZSB1bmFjY291bnRlZCBkYXRlcyBmb3IgZWFjaCBjbGFzcwp1bmFjY291bnRlZF9kYXRlc19saXN0IDwtIGxpc3QoKQoKIyBJdGVyYXRlIG92ZXIgZWFjaCBjbGFzcyBvYmplY3QKZm9yIChjbGFzc19uYW1lIGluIGNsYXNzX25hbWVzKSB7CiAgIyBHZXQgdGhlIGRhdGUtdGltZSBvYmplY3RzIGZyb20gdGhlIGN1cnJlbnQgY2xhc3MKICBzZWxfY2xhc3NfZGF0ZXMgPC0gZ2V0KHBhc3RlMCgiY2xhc3MuIiwgY2xhc3NfbmFtZSkpJGRhdGUKICAKICAjIEluaXRpYWxpemUgYSBsb2dpY2FsIHZlY3RvciB0byB0cmFjayB3aGV0aGVyIGVhY2ggY2xhc3MgZGF0ZSBpcyBhY2NvdW50ZWQgZm9yCiAgaXNfYWNjb3VudGVkX2ZvciA8LSByZXAoRkFMU0UsIGxlbmd0aChzZWxfY2xhc3NfZGF0ZXMpKQogIAogICMgQ2hlY2sgZWFjaCBjbGFzcyBkYXRlIGFnYWluc3QgdGhlIGludGVydmFscyBpbiBkdF9saXN0CiAgZm9yIChpIGluIDE6bGVuZ3RoKGR0X2xpc3QpKSB7CiAgICBpZiAoaSA8IGxlbmd0aChkdF9saXN0KSkgewogICAgICAjIENoZWNrIGFsbCBpbnRlcnZhbHMgZXhjZXB0IHRoZSBsYXN0IG9uZQogICAgICBpbnRlcnZhbF9zdGFydCA8LSBkdF9saXN0W2ldIC0gc3RhcnQKICAgICAgaW50ZXJ2YWxfZW5kIDwtIGR0X2xpc3RbaSArIDFdIC0gc3RvcAogICAgfSBlbHNlIHsKICAgICAgIyBMYXN0IGludGVydmFsIGNhcHR1cmVzIGFsbCBkYXRhIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byB0aGUgbGFzdCBkdF9saXN0CiAgICAgIGludGVydmFsX3N0YXJ0IDwtIGR0X2xpc3RbaV0gLSBzdGFydAogICAgICBpbnRlcnZhbF9lbmQgPC0gSW5mICAjIEVmZmVjdGl2ZWx5IG5vIHVwcGVyIGJvdW5kCiAgICB9CiAgICAKICAgICMgTWFyayBjbGFzcyBkYXRlcyB0aGF0IGZhbGwgd2l0aGluIHRoZSBjdXJyZW50IGludGVydmFsIGFzIGFjY291bnRlZCBmb3IKICAgIGlzX2FjY291bnRlZF9mb3IgPC0gaXNfYWNjb3VudGVkX2ZvciB8IChzZWxfY2xhc3NfZGF0ZXMgPj0gaW50ZXJ2YWxfc3RhcnQgJiBzZWxfY2xhc3NfZGF0ZXMgPCBpbnRlcnZhbF9lbmQpCiAgfQogIAogICMgU3Vic2V0IHRoZSBjbGFzcyBkYXRlcyB0aGF0IHdlcmUgbm90IGFjY291bnRlZCBmb3IKICB1bmFjY291bnRlZF9zZWxfY2xhc3NfZGF0ZXMgPC0gc2VsX2NsYXNzX2RhdGVzWyFpc19hY2NvdW50ZWRfZm9yXQogIAogICMgU3RvcmUgdGhlIHVuYWNjb3VudGVkIGRhdGVzIGluIHRoZSBsaXN0CiAgdW5hY2NvdW50ZWRfZGF0ZXNfbGlzdFtbY2xhc3NfbmFtZV1dIDwtIHVuYWNjb3VudGVkX3NlbF9jbGFzc19kYXRlcwp9CgojIFByaW50IG9yIHZpZXcgdGhlIHVuYWNjb3VudGVkIGRhdGVzIGZvciBlYWNoIGNsYXNzCmZvciAoY2xhc3NfbmFtZSBpbiBjbGFzc19uYW1lcykgewogIGNhdCgiVW5hY2NvdW50ZWQgZGF0ZXMgZm9yIGNsYXNzOiIsIGNsYXNzX25hbWUsICJcbiIpCiAgcHJpbnQodW5hY2NvdW50ZWRfZGF0ZXNfbGlzdFtbY2xhc3NfbmFtZV1dKQogIGNhdCgiXG4iKQp9CmBgYAoKIyAjIENhbGN1bGF0ZSBwbGFua3RvbiBjb25jZW50cmF0aW9uIHRpbWUgc2VyaWVzIHBlciBzdGF0aW9uCmBgYHtyfQojIENhbGN1bGF0ZSBzcGVjaWVzIGNvbmNlbnRyYXRpb24gKGNvdW50cy9tbCkgZm9yIGVhY2ggc3BlY2llcwoKIyAjIEZvciB6b29wbGFua3RvbgojIHRheGFfbWV0YV9jb25jZW50cmF0aW9uIDwtIHRheGFfbWV0YSAlPiUKIyAgIG11dGF0ZShhY3Jvc3MoYyhBY2FudGhhcmVhLENoYWV0b2duYXRocyxPc3RyYWNvZHMsQ29wZXBvZHMsRGVjYXBvZHMsRWNoaW5vZGVybXMsSmVsbGllcywKIyAgICAgTGFydmFjZWFucyxQb2x5Y2hhZXRzLFB0ZXJvcG9kcyksIH4gLi90b3RhbF92b2xfc2FtcGxlZCAqIDFlNikpICU+JQojICAgc2VsZWN0KFN0YXRpb24sIGRlY19sYXQsIGRlY19sb24sIHllYXIsIG1vbnRoLCBkYXRlLCBBdmcuY2hsLmEuLnVnLkwuLAojICAgICAgICAgIHRlbXAuLmRlZ0MuLCBzYWxpbml0eSwgdG90YWxfdm9sX3NhbXBsZWQsCiMgICAgICAgICAgQWNhbnRoYXJlYSxDaGFldG9nbmF0aHMsT3N0cmFjb2RzLENvcGVwb2RzLERlY2Fwb2RzLEVjaGlub2Rlcm1zLAojICAgICAgICAgIEplbGxpZXMsTGFydmFjZWFucyxQb2x5Y2hhZXRzLFB0ZXJvcG9kcykgJT4lCiMgICBmaWx0ZXIoIWlzLm5hKHRvdGFsX3ZvbF9zYW1wbGVkKSkKIyAKIyAjICMgVHJhbnNmb3JtIHRheGFfbWV0YV9jb25jZW50cmF0aW9uIHRvIGxvbmcgZm9ybWF0CiMgdGF4YV9tZXRhX2xvbmcgPC0gdGF4YV9tZXRhX2NvbmNlbnRyYXRpb24gJT4lCiMgICBwaXZvdF9sb25nZXIoY29scyA9IGMoQWNhbnRoYXJlYSwgQ2hhZXRvZ25hdGhzLE9zdHJhY29kcyxDb3BlcG9kcyxEZWNhcG9kcywKIyAgICAgICAgICAgICAgICAgICAgICAgICBFY2hpbm9kZXJtcywgSmVsbGllcywgTGFydmFjZWFucyxQb2x5Y2hhZXRzLCBQdGVyb3BvZHMpLAojICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInNwZWNpZXMiLCB2YWx1ZXNfdG8gPSAic3BlY2llc19jb25jZW50cmF0aW9uIikKCiMgRm9yIHBoeXRvcGxhbmt0b24KdGF4YV9tZXRhX2NvbmNlbnRyYXRpb24gPC0gdGF4YV9tZXRhICU+JQogIG11dGF0ZShhY3Jvc3MoYyhDZW50cmljLENlcmF0aXVtLENoYWV0b2Nlcm9zLENoYWluMixDaGFpbjMsR3VpbmFyZGlhLE5lb2NhbHlwdHJlbGxhLAogICAgICAgICAgICAgICAgICBOb2N0aWx1Y2EsVHJpY2hvKSwgfiAuL3RvdGFsX3ZvbF9zYW1wbGVkICogMWU2KSkgJT4lCiAgc2VsZWN0KFN0YXRpb24sIGRlY19sYXQsIGRlY19sb24sIHllYXIsIG1vbnRoLCBkYXRlLCBBdmcuY2hsLmEuLnVnLkwuLAogICAgICAgICB0ZW1wLi5kZWdDLiwgc2FsaW5pdHksdG90YWxfdm9sX3NhbXBsZWQsCiAgICAgICAgIENlbnRyaWMsQ2VyYXRpdW0sQ2hhZXRvY2Vyb3MsQ2hhaW4yLENoYWluMyxHdWluYXJkaWEsTmVvY2FseXB0cmVsbGEsCiAgICAgICAgICAgICAgICAgIE5vY3RpbHVjYSxUcmljaG8pICU+JQogIGZpbHRlcighaXMubmEodG90YWxfdm9sX3NhbXBsZWQpKQoKIyAjIFRyYW5zZm9ybSB0YXhhX21ldGFfY29uY2VudHJhdGlvbiB0byBsb25nIGZvcm1hdAp0YXhhX21ldGFfbG9uZyA8LSB0YXhhX21ldGFfY29uY2VudHJhdGlvbiAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IGMoQ2VudHJpYyxDZXJhdGl1bSxDaGFldG9jZXJvcyxDaGFpbjIsQ2hhaW4zLEd1aW5hcmRpYSxOZW9jYWx5cHRyZWxsYSwKICAgICAgICAgICAgICAgICAgTm9jdGlsdWNhLFRyaWNobyksCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInNwZWNpZXMiLCB2YWx1ZXNfdG8gPSAic3BlY2llc19jb25jZW50cmF0aW9uIikKCiMgRmlsdGVyIHRoZSBkYXRhIGZvciBTdGF0aW9ucwojIHN0YXRpb25fbGlzdF9mayA8LSBjKCJXUyIsIjIxL0xLIiwiTVIiLCIxNiIsIjE4IiwiMTAiLCIxMiIsIjkuNSIsIjkiLCI3IiwiMiIpCiMgc3RhdGlvbl9saXN0X3NyIDwtIGMoIjY4IiwiNjUiLCI2NCIsIjYwIiwiNTgiLCI1Ny4zIiwiNTcuMiIsIjU3LjEiLCI1NyIsIjU2IiwiNTUiLCI1NCIsIjUzIiwiNTEiLAojICAgICAgICAgICAgICAgICAgICI0OSIsIjQ3IiwiNDUiLCI0MSIsIjMwIiwiMzEiLCIzMyIpCiMgc3RhdGlvbl9saXN0X2NhbCA8LSBjKCJDQUw1IiwiQ0FMNCIsIkNBTDMiLCJDQUwyIiwiQ0FMMSIsIlJQMSIsIlJQMiIsIlJQMyIsIlJQNCIsIkdQNSIsIkJHNCIsIkJHMyIsCiMgICAgICAgICAgICAgICAgICAgIkJHMiIsIkJHMSIsIkJHNiIsICJCRzciKQojIHN0YXRpb25fbGlzdF92bCA8LSBjKCJWMSIsIlYyIiwiVjMiLCJWNCIsIlY1IiwiVjYiLCJWNyIsIlY4IiwiVjkiLCJMMSIsIkwzIiwiTDUiLCJMNyIsIkw5IikKIyBzdGF0aW9uX2xpc3RfdGIgPC0gYygiQU1JOSIsIkFNSTgiLCJBTUk3IiwiQU1JNiIsIkFNSTUiLCJBTUk0IiwiQU1JMyIsIkFNSTIiLCJBTUkxIiwiVEIxIiwiVEIyIiwKIyAgICAgICAgICAgICAgICAgICAiVEIzIiwiVEI0IiwiVEI1IiwiVEIxMCIsIkNXNCIsIkNXMyIsIkNXMiIsIkNXMSIpCnN0YXRpb25fc2VsZWN0ZWQgPC0gIjMxIgpmaWx0ZXJlZF90YXhhX21ldGFfbG9uZyA8LSB0YXhhX21ldGFfbG9uZyAlPiUKZmlsdGVyKFN0YXRpb24gJWluJSBzdGF0aW9uX3NlbGVjdGVkKQoKIyAjIFdpdGhvdXQgZmlsdGVyaW5nIHBlciBncm91cCBvZiBzdGF0aW9ucyBidXQgbG9va2luZyBhdCB0aGUgZW50aXJlIHJlZ2lvbgojIGZpbHRlcmVkX3RheGFfbWV0YV9sb25nIDwtIHRheGFfbWV0YV9sb25nCgojIENhbGN1bGF0ZSBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gb2Ygc3BlY2llcyBjb25jZW50cmF0aW9uIGZvciBlYWNoIGRhdGUKc3VtbWFyeV9kYXRhIDwtIGZpbHRlcmVkX3RheGFfbWV0YV9sb25nICU+JQogIGdyb3VwX2J5KHllYXIsIG1vbnRoLCBzcGVjaWVzKSAlPiUKICBzdW1tYXJpc2UobWVhbl9jb25jZW50cmF0aW9uID0gbWVhbihzcGVjaWVzX2NvbmNlbnRyYXRpb24sIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIHNkX2NvbmNlbnRyYXRpb24gPSBzZChzcGVjaWVzX2NvbmNlbnRyYXRpb24sIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIGVhcmxpZXN0X2RheSA9IG1pbihhcy5udW1lcmljKGZvcm1hdChkYXRlLCAiJWQiKSksIG5hLnJtID0gVFJVRSkpICU+JQogIHVuZ3JvdXAoKQoKIyBDb21iaW5lIHllYXIgYW5kIG1vbnRoIGNvbHVtbnMgaW50byBhIHNpbmdsZSBkYXRlIGNvbHVtbgpzdW1tYXJ5X2RhdGEkZGF0ZSA8LSBhcy5EYXRlKHBhc3RlKHN1bW1hcnlfZGF0YSR5ZWFyLCBzdW1tYXJ5X2RhdGEkbW9udGgsIHN1bW1hcnlfZGF0YSRlYXJsaWVzdF9kYXksIHNlcCA9ICItIikpIAoKIyBjdXN0b21fcGFsX2hleDIgPC0gYygnI2U0MWExYycsJyMzNzdlYjgnLCcjNGRhZjRhJywnIzk4NGVhMycsJyNmZjdmMDAnLCcjZmZmZjMzJywnI2E2NTYyOCcsJyNmNzgxYmYnKQpjdXN0b21fcGFsX2hleDIgPC0gYygnZGFya3R1cnF1b2lzZScsJ3JlZCcsJyM0ZGFmNGEnLCdzbGF0ZWdyYXk0Jywnb3JhbmdlJywnZG9kZ2VyYmx1ZScsJ3B1cnBsZScsICJ5ZWxsb3ciLCJwaW5rIiwidmlvbGV0IikKCiMgQWNhbnRoYXJlYSA9IGRhcmt0dXJxdW9pc2UKIyBDb3BlcG9kcyA9IHJlZAojIGVjaGlub3MgPSAjNGRhZjRhCiMgSmVsbGllcyA9IHNsYXRlZ3JheTQKIyBMYXJ2YWNlYW5zID0gb3JhbmdlCiMgUG9seWNoYWV0ZXMgPSBkb2RnZXJibHVlCiMgUHRlcm9wb2RzID0gcHVycGxlCgojICMgQ2hlY2sgZG9taW5hbmNlIG9mIGdyb3VwcyBieSBjYWxjdWxhdGluZyB0aGUgb3ZlcmFsbCBtZWFuIAojIHRlc3QgPC0gc3VtbWFyeV9kYXRhICU+JSBmaWx0ZXIoc3BlY2llcyA9PSAiUG9seWNoYWV0ZXMiKSAlPiUgc3VtbWFyaXplKGF2ZyA9IG1lYW4obWVhbl9jb25jZW50cmF0aW9uKSkKIyBtZWFuKHRlc3QkYXZnKQoKIyBQbG90IHRpbWUgc2VyaWVzIG9mIG1lYW4rc2QgcGxhbnRvbiBjb25jZW50cmF0aW9uIHBlciBncm91cCBvZiBzaXRlcwp6eiA8LSBnZ3Bsb3Qoc3VtbWFyeV9kYXRhLCBhZXMoeCA9IGRhdGUsIHkgPSBtZWFuX2NvbmNlbnRyYXRpb24sIGZpbGwgPSBzcGVjaWVzKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBhbHBoYSA9IDAuNykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9wYWxfaGV4MikgKwogICMgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IDAsIHltYXggPSBtZWFuX2NvbmNlbnRyYXRpb24gKyBzZF9jb25jZW50cmF0aW9uKSwKICAjICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuOSksIHdpZHRoID0gMC4yKSArCiAgbGFicyh4ID0gIkRhdGUiLCB5ID0gIk1lYW4gU3BlY2llcyBDb25jZW50cmF0aW9uIiwgZmlsbCA9ICJTcGVjaWVzIikgKwogIHRoZW1lX21pbmltYWwoKQp6egoKIyBGaWx0ZXIgc3VtbWFyeV9kYXRhIGZvciBzZWxlY3RlZCBvbmx5CiMgQWNhbnRoYXJlYSwgQ29wZXBvZHMsIEVjaGlub2Rlcm1zLCBKZWxsaWVzLCBMYXJ2YWNlYW5zLCBDaGFldG9nbmF0aHMsIFBvbHljaGFldGVzLCBQdGVyb3BvZHMKIyBDZW50cmljLENlcmF0aXVtLENoYWV0b2Nlcm9zLENoYWluMixDaGFpbjMsR3VpbmFyZGlhLE5lb2NhbHlwdHJlbGxhLE5vY3RpbHVjYSxUcmljaG9kZXNtaXVtCgpzZWxlY3RlZF9ncm91cCA8LSAiQ2hhaW4zIgoKc3VtbWFyeV9zZWxlY3RlZCA8LSBmaWx0ZXJlZF90YXhhX21ldGFfbG9uZyAlPiUKICBmaWx0ZXIoc3BlY2llcyA9PSBzZWxlY3RlZF9ncm91cCkgJT4lCiAgZmlsdGVyKHNwZWNpZXNfY29uY2VudHJhdGlvbiA+IDApICU+JQogIGdyb3VwX2J5KHllYXIsIG1vbnRoKSAlPiUKICBtdXRhdGUoZWFybGllc3RfZGF5ID0gbWluKGFzLm51bWVyaWMoZm9ybWF0KGRhdGUsICIlZCIpKSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShkYXRlID0gYXMuRGF0ZShwYXN0ZSh5ZWFyLCBtb250aCwgZWFybGllc3RfZGF5LCBzZXAgPSAiLSIpKSkgJT4lCiAgc2VsZWN0KC1lYXJsaWVzdF9kYXkpICAjIE9wdGlvbmFsbHksIHJlbW92ZSB0aGUgJ2VhcmxpZXN0X2RheScgY29sdW1uIGlmIG5vdCBuZWVkZWQKCiMgQ3JlYXRlIHRoZSB0aW1lIHNlcmllcyBib3hwbG90CiAgc2VsX2JveCA8LSAgZ2dwbG90KHN1bW1hcnlfc2VsZWN0ZWQsIGFlcyh4ID0gYXMuZmFjdG9yKGRhdGUpLCB5ID0gc3BlY2llc19jb25jZW50cmF0aW9uKSkgKwogICAgZ2VvbV9ib3hwbG90KGZpbGwgPSAid2hpdGUiLCBub3RjaCA9IEZBTFNFLCBvdXRsaWVyLnNoYXBlID0gTkEpICsKICAgICMgZ2VvbV92aW9saW4oZmlsbCA9ICJsaWdodGdyZXkiLCBjb2xvciA9ICJOQSIsIGFscGhhID0gMC43KSArCiAgICAjIGdlb21faml0dGVyKGFlcyhjb2xvciA9IGZhY3RvcihTdGF0aW9uKSwgc2l6ZSA9IHRlbXAuLmRlZ0MuKSwgYWxwaGEgPSAwLjkpICsKICAgICMgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjI1LCBhZXMoY29sb3IgPSB0ZW1wLi5kZWdDLiwgc2l6ZSA9IHNhbGluaXR5KSwgYWxwaGEgPSAwLjYpICsgIyB3aXRoIHNhbGluaXR5IGluY2x1ZGVkCiAgICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMjUsIGFlcyhjb2xvciA9IHRlbXAuLmRlZ0MuKSwgc2l6ZSA9IDMsIGFscGhhID0gMC42KSArCiAgICBsYWJzKHggPSAiRGF0ZSIsIHkgPSBleHByZXNzaW9uKCJEZW5zaXR5IChvcmcuIG0iXiItMyJ+IikiKSkgKwogICAgc2NhbGVfY29sb3JfdmlyaWRpc19jKG5hbWUgPSAiVGVtcGVyYXR1cmUgKMKwQykiLCBvcHRpb24gPSAiaW5mZXJubyIpICsKICAgICMgc2NhbGVfc2l6ZV9jb250aW51b3VzKG5hbWUgPSAiU2FsaW5pdHkiLCAjIHdpdGggc2FsaW5pdHkgaW5jbHVkZWQKICAgICMgICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMzAsIDM4KSwKICAgICMgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoMzAsIDMyLCAzNCwgMzYsIDM4KSwKICAgICMgICAgICAgICAgICAgICAgICAgICAgIHJhbmdlID0gYygxLCA0KSkgKwogICAgIyB5bGltKDAsIDMwMDAwKSArCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4LCBjb2xvciA9ICJibGFjayIpLCAgIyBTZXQgWC1heGlzIGxhYmVsIGZvbnQgc2l6ZQogICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4LCBjb2xvciA9ICJibGFjayIpKSArCiAgICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4LCBjb2xvciA9ICJibGFjayIpLAogICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCwgY29sb3IgPSAiYmxhY2siKSkgKwogICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSkgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKwogICAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsYWNrIikpIAogIHNlbF9ib3gKCiMgU2F2ZSBmaWd1cmUgYXMgYSBzdmcgZmlsZSAgCiMgZ2dzYXZlKHBhc3RlMCgiY29uY190aW1lLXNlcmllc18iLCBzZWxlY3RlZF9ncm91cCwgIi5zdmciKSwgcGxvdCA9IHNlbF9ib3gsIHdpZHRoID0gMTAsIGhlaWdodCA9IDgsIGRldmljZSA9ICJzdmciKQogIAojICMgUGxvdCBjb25jZW50cmF0aW9uIHRpbWUgc2VyaWVzIGZvciBhbGwgc2VsZWN0ZWQgc2l0ZXMKIyBrayA8LSBnZ3Bsb3Qoc3VtbWFyeV9zZWxlY3RlZCwgYWVzKHggPSBkYXRlLCB5ID0gc3BlY2llc19jb25jZW50cmF0aW9uLCBzaGFwZSA9IGZhY3RvcihTdGF0aW9uKSkpICsKIyAgIGxhYnMoeCA9ICJEYXRlIiwgeSA9ICJTcGVjaWVzIGNvbmNlbnRyYXRpb24gKG9yZy5tLTMpIikgKwojICAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IHRlbXAuLmRlZ0MuKSwgZmlsbCA9ICJkYXJrZ3JleSIpICsgIyBvciBjb2xvdXIgPSBmYWN0b3IoU3RhdGlvbikKIyAgIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDAsIDEsIDIsIDMsIDQsIDUsIDYpKQojIGtrCgojIGtrIDwtIGdncGxvdChzdW1tYXJ5X3NlbGVjdGVkLCBhZXMoeCA9IGRhdGUsIHkgPSBzcGVjaWVzX2NvbmNlbnRyYXRpb24sIGNvbG91ciA9IGZhY3RvcihTdGF0aW9uKSkpICsKIyAgIGxhYnMoeCA9ICJEYXRlIiwgeSA9ICJTcGVjaWVzIGNvbmNlbnRyYXRpb24gKG9yZy5tLTMpIikgKwojICAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IHRlbXAuLmRlZ0MuKSwgYWxwaGEgPSAwLjcpICsgIyBvciBjb2xvdXIgPSBmYWN0b3IoU3RhdGlvbikgKwojICAgIyBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geSB+IHBvbHkoeCwgNCksIHNlID0gRkFMU0UpICsgIyBBZGQgcG9seW5vbWlhbCBmaXQgbGluZQojICAgc2NhbGVfY29sb3VyX2JyZXdlcihwYWxldHRlID0gIlNldDEiKQojIGtrCiAgCiMgIyBGaW5kIHRoZSBpbmRleCBvZiB0aGUgbGFzdCByb3cgd2l0aCBkYXRlID09ICcyMDIzLTAxLTE1JyBhbmQgYWRkIGEgZHVtbXkgcm93IGZvciBtaXNzaW5nIGRhdGVzCiMgbmV3X3JvdyA8LSB0aWJibGUoU3RhdGlvbiA9IE5BLCBkZWNfbGF0ID0gTkEsIGRlY19sb24gPSBOQSwgeWVhciA9IE5BLAojICAgICAgICAgICAgICAgICAgIG1vbnRoID0gTkEsIGRhdGUgPSBhcy5EYXRlKCcyMDIzLTAzLTE1JyksIEF2Zy5jaGwuYS4udWcuTC4gPSBOQSwKIyAgICAgICAgICAgICAgICAgICB0ZW1wLi5kZWdDLiA9IE5BLCBzYWxpbml0eSA9IE5BLCBzcGVjaWVzID0gTkEsIHNwZWNpZXNfY29uY2VudHJhdGlvbiA9IE5BKQojIGxhc3RfaW5kZXggPC0gbWF4KHdoaWNoKHN1bW1hcnlfc2VsZWN0ZWQkZGF0ZSA9PSBhcy5EYXRlKCcyMDIzLTAxLTE1JykpKQojIHN1bW1hcnlfc2VsZWN0ZWQgPC0gYmluZF9yb3dzKAojICAgICBzdW1tYXJ5X3NlbGVjdGVkWzE6bGFzdF9pbmRleCwgXSwKIyAgICAgbmV3X3JvdywKIyAgICAgc3VtbWFyeV9zZWxlY3RlZFsobGFzdF9pbmRleCArIDEpOm5yb3coc3VtbWFyeV9zZWxlY3RlZCksIF0KIyAgICkKICAKYGBgCgojICMgQ2FsY3VsYXRlcyB0b3RhbCBwbGFua3RvbiBjb25jZW50cmF0aW9ucyBhZ2dyZWdhdGluZyBkYXRhIGZyb20gc2VsZWN0ZWQgc3RhdGlvbnMgKGVnIEZLKQpgYGB7cn0KIyBDYWxjdWxhdGUgcGxhbmt0b24gY29uY2VudHJhdGlvbiBmb3IgYWxsIHNwZWNpZXMgc3VtbWVkIHVwCiMgRm9yIFBoeXRvCnRheGFfbWV0YV9jb25jZW50cmF0aW9uX2FsbCA8LSB0YXhhX21ldGEgJT4lCiAgbXV0YXRlKHRvdGFsX2NvbmNlbnRyYXRpb24gPSAoCiAgICBDZW50cmljICsgQ2VyYXRpdW0gKyBDaGFldG9jZXJvcyArIENoYWluMiArIENoYWluMyArIEd1aW5hcmRpYSArIE5lb2NhbHlwdHJlbGxhICsKICAgIE5vY3RpbHVjYSArIFRyaWNobykgLyB0b3RhbF92b2xfc2FtcGxlZCAqIDFlNikgJT4lCiAgc2VsZWN0KFN0YXRpb24sIGRlY19sYXQsIGRlY19sb24sIHllYXIsIG1vbnRoLCBkYXRlLCBBdmcuY2hsLmEuLnVnLkwuLAogICAgICAgICB0ZW1wLi5kZWdDLiwgc2FsaW5pdHksIHRvdGFsX3ZvbF9zYW1wbGVkLCB0b3RhbF9jb25jZW50cmF0aW9uKSAlPiUKICBmaWx0ZXIoIWlzLm5hKHRvdGFsX3ZvbF9zYW1wbGVkKSkKCiMgIyBGb3IgWm9vcGxhbmt0b24KIyB0YXhhX21ldGFfY29uY2VudHJhdGlvbl9hbGwgPC0gdGF4YV9tZXRhICU+JQojICAgbXV0YXRlKHRvdGFsX2NvbmNlbnRyYXRpb24gPSAoCiMgICAgIEFjYW50aGFyZWEgKyBDaGFldG9nbmF0aHMgKyBPc3RyYWNvZHMgKyBDb3BlcG9kcyArIERlY2Fwb2RzICsgRWNoaW5vZGVybXMgKyBKZWxsaWVzICsKIyAgICAgTGFydmFjZWFucyArIFBvbHljaGFldHMgKyBQdGVyb3BvZHMpIC8gdG90YWxfdm9sX3NhbXBsZWQgKiAxZTYpICU+JQojICAgc2VsZWN0KFN0YXRpb24sIGRlY19sYXQsIGRlY19sb24sIHllYXIsIG1vbnRoLCBkYXRlLCBBdmcuY2hsLmEuLnVnLkwuLAojICAgICAgICAgIHRlbXAuLmRlZ0MuLCBzYWxpbml0eSwgdG90YWxfdm9sX3NhbXBsZWQsIHRvdGFsX2NvbmNlbnRyYXRpb24pICU+JQojICAgZmlsdGVyKCFpcy5uYSh0b3RhbF92b2xfc2FtcGxlZCkpCgojIEZpbHRlciB0aGUgZGF0YSBmb3IgU3RhdGlvbnMKc3RhdGlvbl9saXN0X2ZrIDwtIGMoIldTIiwiMjEvTEsiLCJNUiIsIjE2IiwiMTgiLCIxMCIsIjEyIiwiOS41IiwiOSIsIjciLCIyIikKZmlsdGVyZWRfdGF4YV9jb25jX2FsbCA8LSB0YXhhX21ldGFfY29uY2VudHJhdGlvbl9hbGwgJT4lCiAgZmlsdGVyKFN0YXRpb24gJWluJSBzdGF0aW9uX2xpc3RfZmspCgojIEFnZ3JlZ2F0ZSB0b3RhbCBjb25jZW50cmF0aW9uIGFuZCB0b3RhbCB2b2x1bWUgc2FtcGxlZCBwZXIgeWVhciBhbmQgbW9udGgKYWdncmVnYXRlZF9jb25jZW50cmF0aW9uIDwtIGZpbHRlcmVkX3RheGFfY29uY19hbGwgJT4lCiAgZ3JvdXBfYnkoeWVhciwgbW9udGgpICU+JQogIHN1bW1hcmlzZSgKICAgIHRvdGFsX2NvdW50cyA9IHN1bSh0b3RhbF9jb25jZW50cmF0aW9uICogdG90YWxfdm9sX3NhbXBsZWQgLyAxZTYsIG5hLnJtID0gVFJVRSksICMgU3VtIHVwIGFsbCBjb3VudHMKICAgIHRvdGFsX3ZvbF9zYW1wbGVkID0gc3VtKHRvdGFsX3ZvbF9zYW1wbGVkLCBuYS5ybSA9IFRSVUUpLCAjIFN1bSB1cCBhbGwgdm9sdW1lIHNhbXBsZWQKICAgIG1lYW5fdGVtcF9kZWdDID0gbWVhbih0ZW1wLi5kZWdDLiwgbmEucm0gPSBUUlVFKSwgIyBDYWxjdWxhdGUgbWVhbiB0ZW1wZXJhdHVyZQogICAgbWVhbl9zYWxpbml0eSA9IG1lYW4oc2FsaW5pdHksIG5hLnJtID0gVFJVRSksICMgQ2FsY3VsYXRlIG1lYW4gc2FsaW5pdHkKICAgIG1lYW5fY2hsYSA9IG1lYW4oQXZnLmNobC5hLi51Zy5MLiwgbmEucm0gPSBUUlVFKSAKICApICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUodG90YWxfY29uY2VudHJhdGlvbiA9ICh0b3RhbF9jb3VudHMgLyB0b3RhbF92b2xfc2FtcGxlZCkgKiAxZTYpICMgUmVjYWxjdWxhdGUgdGhlIGNvbmNlbnRyYXRpb24KCiMgQ3JlYXRlIGEgRGF0ZSBjb2x1bW4gZm9yIHBsb3R0aW5nIHB1cnBvc2VzCmFnZ3JlZ2F0ZWRfY29uY2VudHJhdGlvbiA8LSBhZ2dyZWdhdGVkX2NvbmNlbnRyYXRpb24gJT4lCiAgbXV0YXRlKHllYXJfbW9udGggPSBhcy5EYXRlKHBhc3RlKHllYXIsIG1vbnRoLCAiMDEiLCBzZXAgPSAiLSIpKSkKCiMgQ3JlYXRlIHRoZSB0aW1lIHNlcmllcyBwbG90CmNvbmNlbnRyYXRpb25fdHMgPC0gZ2dwbG90KGFnZ3JlZ2F0ZWRfY29uY2VudHJhdGlvbiwgYWVzKHggPSB5ZWFyX21vbnRoKSkgKwogIGdlb21fbGluZShhZXMoeSA9IHRvdGFsX2NvbmNlbnRyYXRpb24pLCBjb2xvciA9ICJibHVlIiwgc2l6ZSA9IDEpICsgIyBMaW5lIHBsb3QgZm9yIHRvdGFsIGNvbmNlbnRyYXRpb24KICBnZW9tX3BvaW50KGFlcyh5ID0gdG90YWxfY29uY2VudHJhdGlvbiksIGNvbG9yID0gInJlZCIsIHNpemUgPSAyKSArICAjIFBvaW50cyBmb3IgdG90YWwgY29uY2VudHJhdGlvbgogICMgZ2VvbV9saW5lKGFlcyh5ID0gKG1lYW5fdGVtcF9kZWdDIC0gMjApIC8gKDM1IC0gMjApICogbWF4KHRvdGFsX2NvbmNlbnRyYXRpb24pKSwgCiAgIyAgICAgICAgICAgY29sb3IgPSAib3JhbmdlIiwgc2l6ZSA9IDEpICsgICMgTGluZSBwbG90IGZvciB0ZW1wZXJhdHVyZSAobm9ybWFsaXplZCkKICBsYWJzKHRpdGxlID0gIlRvdGFsIFBoeXRvcGxhbmt0b24gQ29uY2VudHJhdGlvbiBUaW1lIFNlcmllcyIsCiAgICAgICB4ID0gIkRhdGUiLAogICAgICAgeSA9IGV4cHJlc3Npb24oIlRvdGFsIENvbmNlbnRyYXRpb24gKG9yZy9tIl4zfiIpIikpICsKICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiMSBtb250aCIsIGRhdGVfbGFiZWxzID0gIiViICVZIikgKyAgIyBTaG93IHRpY2tzIGZvciBldmVyeSBtb250aAogIHNjYWxlX3lfY29udGludW91cygKICAgIG5hbWUgPSBleHByZXNzaW9uKCJUb3RhbCBDb25jZW50cmF0aW9uIChvcmcvbSJeM34iKSIpLCAgICAgICAgICAgICMgUHJpbWFyeSB5LWF4aXMgbGFiZWwKICAgICMgc2VjLmF4aXMgPSBzZWNfYXhpcyh+IC4gKiAoMzUgLSAyMCkgLyBtYXgoYWdncmVnYXRlZF9jb25jZW50cmF0aW9uJHRvdGFsX2NvbmNlbnRyYXRpb24pICsgMjAsIAogICAgIyAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiVGVtcGVyYXR1cmUgKMKwQykiLCAKICAgICMgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoMjAsIDM1LCBieSA9IDUpKSAgIyBTZWNvbmRhcnkgeS1heGlzIGxhYmVsCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICAjIGF4aXMudGl0bGUueS5yaWdodCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJvcmFuZ2UiKSwgICMgQ29sb3IgdGhlIHNlY29uZGFyeSB5LWF4aXMgbGFiZWwKICAgICMgYXhpcy5saW5lLnkucmlnaHQgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAib3JhbmdlIiksICAgICMgQ29sb3IgdGhlIHNlY29uZGFyeSB5LWF4aXMgbGluZSAgIAogICAgIyBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAgIyBSZW1vdmUgbWFqb3IgZ3JpZCBsaW5lcwogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgICMgUmVtb3ZlIG1pbm9yIGdyaWQgbGluZXMKICAgICMgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpICAgICAgICMgUmVtb3ZlIHBhbmVsIGJvcmRlciBpZiBuZWNlc3NhcnkKICApCgojIFByaW50IHRoZSBwbG90CmNvbmNlbnRyYXRpb25fdHMKYGBgCgojICMgQ2FsY3VsYXRlIHBsYW5rdG9uIGNvbmNlbnRyYXRpb25zIHBlciBzZWFzY2FwZSBjbGFzcwpgYGB7cn0Kc3BwX2RmIDwtIHRheGFfbWV0YVsgLCBjKCJYOC5kYXkuc2Vhc2NhcGVzIiwgInRvdGFsX3ZvbF9zYW1wbGVkIiwgImRhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgIkFjYW50aGFyZWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgIkNvcGVwb2RzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICJFY2hpbm9kZXJtcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiSmVsbGllcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiTGFydmFjZWFucyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiUG9seWNoYWV0cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhZXRvZ25hdGhzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICJQdGVyb3BvZHMiKV0KCiMgc3BwX2RmIDwtIHRheGFfbWV0YVsgLCBjKCJYOC5kYXkuc2Vhc2NhcGVzIiwgInRvdGFsX3ZvbF9zYW1wbGVkIiwgImRhdGUiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2VyYXRpdW0iLAojICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhZXRvY2Vyb3MiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhaW4yIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIkNoYWluMyIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICJHdWluYXJkaWEiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAiTmVvY2FseXB0cmVsbGEiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJpY2hvIildCgojIFJlbW92ZSByb3dzIHdpdGggTmFOIHZhbHVlcyBpbiBYOC5kYXkuc2Vhc2NhcGVzIGFuZCB0b3RhbF92b2xfc2FtcGxlZApzcHBfZGYgPC0gc3Vic2V0KHNwcF9kZiwgIWlzLm5hKFg4LmRheS5zZWFzY2FwZXMpICYgIWlzLm5hKHRvdGFsX3ZvbF9zYW1wbGVkKSkKCiMgQ2FsY3VsYXRlIHRvdGFsIGNvdW50cyBwZXIgc3BlY2llcyB3aXRoaW4gZWFjaCBYOC5kYXkuc2Vhc2NhcGVzIGNhdGVnb3J5CnNwcF9jb3VudF9wZXJfc2Vhc2NhcGUgPC0gc3BwX2RmICU+JQogIGdhdGhlcihrZXkgPSAic3BlY2llcyIsIHZhbHVlID0gImNvdW50IiwgLShYOC5kYXkuc2Vhc2NhcGVzOmRhdGUpKSAlPiUKICBncm91cF9ieShYOC5kYXkuc2Vhc2NhcGVzLCBzcGVjaWVzKSAlPiUKICBzdW1tYXJpc2UodG90YWxfY291bnQgPSBzdW0oY291bnQpKSAlPiUKICB1bmdyb3VwKCkKCiMgQ2FsY3VsYXRlIHRvdGFsIHZvbHVtZSBzYW1wbGVkIHBlciBYOC5kYXkuc2Vhc2NhcGVzIGNhdGVnb3J5CnRvdGFsX3ZvbF9wZXJfc2Vhc2NhcGUgPC0gc3BwX2RmICU+JQogIGdyb3VwX2J5KFg4LmRheS5zZWFzY2FwZXMpICU+JQogIHN1bW1hcmlzZSh0b3RhbF92b2xfc2FtcGxlZCA9IHN1bSh0b3RhbF92b2xfc2FtcGxlZCkpCgojIE1lcmdlIHRvdGFsIGNvdW50cyBhbmQgdG90YWwgdm9sdW1lIGRhdGEgZnJhbWVzCnNwcF9jb25jZW50cmF0aW9uIDwtIG1lcmdlKHNwcF9jb3VudF9wZXJfc2Vhc2NhcGUsIHRvdGFsX3ZvbF9wZXJfc2Vhc2NhcGUsIGJ5ID0gIlg4LmRheS5zZWFzY2FwZXMiKQoKIyBDYWxjdWxhdGUgc3BlY2llcyBwZXIgY3ViaWMgbWV0ZXI6IG1pZ2h0IGJlIG1pc2xlYWRpbmcgc2luY2UgaXQgaW50ZWdyYXRlcyBhbGwgY291bnRzIGFuZCBzYW1wbGVkIHZvbHVtZXMgYWNyb3NzIGNydWlzZXMgdG8gY2FsY3VsYXRlIGNvbmNlbnRyYXRpb25zLiBJdCBpcyBsaWtlbHkgYmV0dGVyIHRvIHVzZSBhdmVyYWdlIGNvbmNlbnRyYXRpb25zIGFzIGJlbG93LgpzcHBfY29uY2VudHJhdGlvbiRzcGVjaWVzX3Blcl9jdWJpY19tZXRlciA8LSBzcHBfY29uY2VudHJhdGlvbiR0b3RhbF9jb3VudCAvIHNwcF9jb25jZW50cmF0aW9uJHRvdGFsX3ZvbF9zYW1wbGVkICogMWU2CgojICMgU2VsZWN0IGRlc2lyZWQgc2Vhc2NhcGVzIGFuZCByZW9yZGVyIGNhdGVnb3JpZXMgaW4gWCBheGlzCnNwcF9jb25jZW50cmF0aW9uJFg4LmRheS5zZWFzY2FwZXMgPC0gZmFjdG9yKHNwcF9jb25jZW50cmF0aW9uJFg4LmRheS5zZWFzY2FwZXMsIGxldmVscyA9IGMoIjMiLCAiNSIsICI3IiwgIjExIiwgIjEzIiwgIjE1IiwiMjEiLCIyNyIpKQoKIyBGaWx0ZXIgb3V0IHNlYXNjYXBlIGNsYXNzIGFzIGRlc2lyZWQKIyBleGNsdWRlX2NsYXNzZXMgPC0gYyg1LCA3LCAxMSkKIyBzcHBfY29uY2VudHJhdGlvbiA8LSBzcHBfY29uY2VudHJhdGlvblshc3BwX2NvbmNlbnRyYXRpb24kWDguZGF5LnNlYXNjYXBlcyAlaW4lIGV4Y2x1ZGVfY2xhc3NlcywgXQoKY3VzdG9tX3BhbF9oZXgyIDwtIGMoJyNlNDFhMWMnLCcjMzc3ZWI4JywnIzRkYWY0YScsJyM5ODRlYTMnLCcjZmY3ZjAwJywnI2ZmZmYzMycsJyNhNjU2MjgnLCcjZjc4MWJmJykKCiMgQ3JlYXRlIHRoZSBzdGFjayBwbG90CmNvbmNlbnRyYXRpb25fc3RhY2twbG90IDwtIGdncGxvdChzcHBfY29uY2VudHJhdGlvbiwgYWVzKHggPSBYOC5kYXkuc2Vhc2NhcGVzLCB5ID0gc3BlY2llc19wZXJfY3ViaWNfbWV0ZXIsIGZpbGwgPSBzcGVjaWVzKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY3VzdG9tX3BhbF9oZXgyKSArCiAgbGFicyh4ID0gIlNlYXNjYXBlIGNsYXNzIiwgeSA9ICJTcGVjaWVzIGNvbmNlbnRyYXRpb24gKG9yZy4gbSJeIi0zIn4iKSIpICsKdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpLCAgIyBTZXQgWC1heGlzIGxhYmVsIGZvbnQgc2l6ZQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCkpICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkgKwogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNikpCmNvbmNlbnRyYXRpb25fc3RhY2twbG90CgojIENhbGN1bGF0ZSBwZXJjZW50YWdlcwpzcHBfY29uY2VudHJhdGlvbl9wZXJjZW50IDwtIHNwcF9jb25jZW50cmF0aW9uICU+JQogIGdyb3VwX2J5KFg4LmRheS5zZWFzY2FwZXMpICU+JQogIG11dGF0ZSh0b3RhbF9jb25jZW50cmF0aW9uID0gc3VtKHNwZWNpZXNfcGVyX2N1YmljX21ldGVyKSwKICAgICAgICAgc3BlY2llc19wZXJjZW50YWdlID0gKHNwZWNpZXNfcGVyX2N1YmljX21ldGVyIC8gdG90YWxfY29uY2VudHJhdGlvbikgKiAxMDApCgojIENyZWF0ZSB0aGUgc3RhY2tlZCBwbG90CmNvbmNlbnRyYXRpb25fcGVyY2VudF9zdGFja3Bsb3QgPC0gZ2dwbG90KHNwcF9jb25jZW50cmF0aW9uX3BlcmNlbnQsIGFlcyh4ID0gWDguZGF5LnNlYXNjYXBlcywgeSA9IHNwZWNpZXNfcGVyY2VudGFnZSwgZmlsbCA9IHNwZWNpZXMpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fcGFsX2hleDIpICsKICBsYWJzKHggPSAiU2Vhc2NhcGUgY2xhc3MiLCB5ID0gIlJlbGF0aXZlIGNvbmNlbnRyYXRpb24gKCUpIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwgICMgU2V0IFgtYXhpcyBsYWJlbCBmb250IHNpemUKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpKSArCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCkpICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpKQpjb25jZW50cmF0aW9uX3BlcmNlbnRfc3RhY2twbG90CgojIEdhdGhlciBjb2x1bW5zIGNvbnRhaW5pbmcgc3BlY2llcyBjb3VudHMgaW50byBrZXktdmFsdWUgcGFpcnMgYW5kIGNhbGN1bGF0ZSBjb25jZW50cmF0aW9ucwpzcHBfY29uY2VudHJhdGlvbl9sb25nIDwtIHNwcF9kZiAlPiUKICBnYXRoZXIoa2V5ID0gInNwZWNpZXMiLCB2YWx1ZSA9ICJjb3VudCIsIC0oWDguZGF5LnNlYXNjYXBlczpkYXRlKSkgJT4lCiAgbXV0YXRlKGNvbmNlbnRyYXRpb24gPSAoY291bnQgLyB0b3RhbF92b2xfc2FtcGxlZCkgKiAxZTYpCgojIENhbGN1bGF0ZSB0aGUgbWVhbiBjb25jZW50cmF0aW9uIGFuZCBpdHMgc3RhbmRhcmQgZGV2aWF0aW9uIHBlciBzcGVjaWVzIGFuZCA4WC5kYXkuc2Vhc2NhcGVzIGNhdGVnb3J5LiBUaGlzIGlzIGxpa2VseSBtb3JlIHJlcHJlc2VudGF0aXZlIHNpbmNlIHRoZSBhcHByb2FjaCB3aWxsIGNhcHR1cmUgaGlnaCBjb25jZW50cmF0aW9uIGV2ZW50cyAoaGlnaCBjb3VudHMgaW4gbG93IHZvbHVtZXMpIHRoYXQgb3RoZXJ3aXNlIGdldCBmaWx0ZXJlZCBvdXQgd2hlbiB1c2luZyBhbiBvdmVyYWxsIGludGVncmF0ZWQgc2FtcGxlZCB2b2x1bWUgYW5kIHRvdGFsIGNvdW50cyBmb3IgdGhlIHRvdGFsIGNvbmNlbnRyYXRpb24uCmF2Z19jb25jZW50cmF0aW9uX3Blcl9jbGFzcyA8LSBzcHBfY29uY2VudHJhdGlvbl9sb25nICU+JQogIGdyb3VwX2J5KHNwZWNpZXMsIGBYOC5kYXkuc2Vhc2NhcGVzYCkgJT4lCiAgc3VtbWFyaXplKAogICAgbWVhbl9jb25jZW50cmF0aW9uID0gbWVhbihjb25jZW50cmF0aW9uLCBuYS5ybSA9IFRSVUUpLAogICAgc2RfY29uY2VudHJhdGlvbiA9IHNkKGNvbmNlbnRyYXRpb24sIG5hLnJtID0gVFJVRSkKICApCgphdmdfY29uY2VudHJhdGlvbl9wZXJfY2xhc3MkWDguZGF5LnNlYXNjYXBlcyA8LSBmYWN0b3IoYXZnX2NvbmNlbnRyYXRpb25fcGVyX2NsYXNzJFg4LmRheS5zZWFzY2FwZXMsIGxldmVscyA9IGMoIjMiLCAiNSIsICI3IiwgIjExIiwgIjEzIiwgIjE1IiwiMjEiLCIyNyIpKQoKYXZnX3N0YWNrcGxvdCA8LSBnZ3Bsb3QoYXZnX2NvbmNlbnRyYXRpb25fcGVyX2NsYXNzLCBhZXMoeCA9IFg4LmRheS5zZWFzY2FwZXMsIHkgPSBtZWFuX2NvbmNlbnRyYXRpb24sIGZpbGwgPSBzcGVjaWVzKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgIyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fcGFsX2hleDIpICsgIyB1c2UgZm9yIHpvb3BsYW5rdG9uCiAgc2NhbGVfY29sb3VyX2JyZXdlcihwYWxldHRlID0gIlNldDEiKSArICMgdXNlIGZvciBwaHl0b3BsYW5rdG9uCiAgbGFicyh4ID0gIlNlYXNjYXBlIGNsYXNzIiwgeSA9ICJNZWFuIGRlbnNpdHkgKG9yZy4gbSJeIi0zIn4iKSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksICAjIFNldCBYLWF4aXMgbGFiZWwgZm9udCBzaXplCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkgKwogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpKSArCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSkKYXZnX3N0YWNrcGxvdAoKIyBDYWxjdWxhdGUgcGVyY2VudGFnZXMKYXZnX2NvbmNlbnRyYXRpb25fcGVyY2VudCA8LSBhdmdfY29uY2VudHJhdGlvbl9wZXJfY2xhc3MgJT4lCiAgZ3JvdXBfYnkoYFg4LmRheS5zZWFzY2FwZXNgKSAlPiUKICBtdXRhdGUobWVhbl9jb25jZW50cmF0aW9uX3BlcmNlbnQgPSBtZWFuX2NvbmNlbnRyYXRpb24gLyBzdW0obWVhbl9jb25jZW50cmF0aW9uKSAqIDEwMCkKCmF2Z19wZXJjZW50X3N0YWNrcGxvdCA8LSBnZ3Bsb3QoYXZnX2NvbmNlbnRyYXRpb25fcGVyY2VudCwgYWVzKHggPSBYOC5kYXkuc2Vhc2NhcGVzLCB5ID0gbWVhbl9jb25jZW50cmF0aW9uX3BlcmNlbnQsIGZpbGwgPSBzcGVjaWVzKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY3VzdG9tX3BhbF9oZXgyKSArICMgdXNlIGZvciB6b29wbGFua3RvbgogICMgc2NhbGVfY29sb3VyX2JyZXdlcihwYWxldHRlID0gIlNldDIiKSArICMgdXNlIGZvciBwaHl0b3BsYW5rdG9uCiAgbGFicyh4ID0gIlNlYXNjYXBlIGNsYXNzIiwgeSA9ICJSZWxhdGl2ZSBjb25jZW50cmF0aW9uICglKSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksICAjIFNldCBYLWF4aXMgbGFiZWwgZm9udCBzaXplCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkgKwogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpKSArCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSkKYXZnX3BlcmNlbnRfc3RhY2twbG90CgpgYGAKCgojIEdlbmVyYXRlIHBsb3RzIApgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoaHJicnRoZW1lcykKbGlicmFyeSh2aXJpZGlzKQoKIyAjIExvYWQgc2Vhc2NhcGUgY29sb3IgcGFsZXR0ZSB1c2VkIHdpdGggTWF0bGFiIGFuZCBleHRyYWN0IFJHQiB2YWx1ZXMgZm9yIG9ic2VydmVkIHVuaXF1ZSBzZWFzY2FwZXMKIyBGb3IgTk9BQSBtYWNoaW5lcwojIHBhbGV0dGVfZGlyIDwtICIvVXNlcnMvZW5yaXF1ZS5tb250ZXMvTGlicmFyeS9DbG91ZFN0b3JhZ2UvR29vZ2xlRHJpdmUtZW5yaXF1ZW1vbnRlczAxQGdtYWlsLmNvbS9NeSBEcml2ZS9HRHJpdmUvc29mdHdhcmUvbWF0bGFiL21fbWFwL3NlYXNjYXBlX2NtIgojIEZvciBwZXJzb25hbCBtYWNoaW5lCnBhbGV0dGVfZGlyIDwtICJ+L0xpYnJhcnkvQ2xvdWRTdG9yYWdlL0dvb2dsZURyaXZlLWVucmlxdWVtb250ZXMwMUBnbWFpbC5jb20vTXkgRHJpdmUvR0RyaXZlL3NvZnR3YXJlL21hdGxhYi9tX21hcC9zZWFzY2FwZV9jbSIKcGFsZXR0ZV9maWxlIDwtIGxpc3QuZmlsZXMocGF0aCA9IHBhbGV0dGVfZGlyLCBwYXR0ZXJuID0gImNtYXAxLmNzdiIsIGZ1bGwubmFtZXMgPSBUUlVFKQpwYWxldHRlX2RmIDwtIHJlYWQuY3N2KHBhbGV0dGVfZmlsZSwgaGVhZGVyID0gRkFMU0UpCmNvbG5hbWVzKHBhbGV0dGVfZGYpIDwtIGMoInIiLCAiZyIsICJiIikKdW5pcXVlX3NlYXNjYXBlcyA8LSBzb3J0KHVuaXF1ZShuYS5vbWl0KGN0ZF9tZXRhJFg4LmRheS5zZWFzY2FwZXMpKSkKc3Vic2V0X3BhbGV0dGVfZGYgPC0gcGFsZXR0ZV9kZlt1bmlxdWVfc2Vhc2NhcGVzLCBdCgojIHNldCBSR0IgdmFsdWVzIGZvciB0aGUgcGxvdHMKcl92YWxzIDwtIHJvdW5kKHN1YnNldF9wYWxldHRlX2RmJHIgKiAyNTUsIDApCmdfdmFscyA8LSByb3VuZChzdWJzZXRfcGFsZXR0ZV9kZiRnICogMjU1LCAwKQpiX3ZhbHMgPC0gcm91bmQoc3Vic2V0X3BhbGV0dGVfZGYkYiAqIDI1NSwgMCkKY3VzdG9tX3BhbCA8LSBjYmluZChyX3ZhbHMsIGdfdmFscywgYl92YWxzKQpjdXN0b21fcGFsX2hleCA8LSByZ2IoY3VzdG9tX3BhbFssIDFdLCBjdXN0b21fcGFsWywgMl0sIGN1c3RvbV9wYWxbLCAzXSwgbWF4Q29sb3JWYWx1ZT0yNTUpCiMgcGFsX2ZpbmFsIDwtIGMoY3VzdG9tX3BhbF9oZXhbM10sIGN1c3RvbV9wYWxfaGV4WzRdLCBjdXN0b21fcGFsX2hleFs1XSwgKQoKIyBmaWx0ZXIgb3V0IHJvd3Mgd2l0aCBOQSB2YWx1ZXMgaW4gY29sdW1uIHNlYXNjYXBlcwpkZl9maWx0ZXJlZCA8LSB0YXhhX21ldGFbY29tcGxldGUuY2FzZXModGF4YV9tZXRhJFg4LmRheS5zZWFzY2FwZXMpLF0KCiMgQ29udmVydCB0aGUgJ3gnIGNvbHVtbiB0byBjaGFyYWN0ZXIKZGZfZmlsdGVyZWQkWDguZGF5LnNlYXNjYXBlcyA8LSBhcy5jaGFyYWN0ZXIoZGZfZmlsdGVyZWQkWDguZGF5LnNlYXNjYXBlcykKCiMgIyBSZW9yZGVyIHNlYXNjYXBlIGNhdGVnb3JpZXMgaW4gWCBheGlzCmRmX2ZpbHRlcmVkJFg4LmRheS5zZWFzY2FwZXMgPC0gZmFjdG9yKGRmX2ZpbHRlcmVkJFg4LmRheS5zZWFzY2FwZXMsIGxldmVscyA9IGMoIjMiLCAiNSIsICI3IiwgIjExIiwgIjEzIiwgIjE1IiwiMjEiLCIyNyIpKQoKIyBEZWZpbmUgY3VzdG9tIGNvbG9ycyBmb3IgZWFjaCBsZXZlbApjdXN0b21fY29sb3JzIDwtIGMoIjMiID0gY3VzdG9tX3BhbF9oZXhbMV0sICI1IiA9IGN1c3RvbV9wYWxfaGV4WzJdLCAiNyIgPSBjdXN0b21fcGFsX2hleFszXSwKICAgICAgICAgICAgICAgICAgICIxMSIgPSBjdXN0b21fcGFsX2hleFs0XSwgIjEzIiA9IGN1c3RvbV9wYWxfaGV4WzVdLCAiMTUiID0gY3VzdG9tX3BhbF9oZXhbNl0sCiAgICAgICAgICAgICAgICAgICAiMjEiID0gY3VzdG9tX3BhbF9oZXhbN10sICIyNyIgPSBjdXN0b21fcGFsX2hleFs4XSkKCiMgRmlsdGVyIG91dCBzZWFzY2FwZSBjbGFzcyBhcyBkZXNpcmVkCiMgZGZfZmlsdGVyZWQgPC0gZGZfZmlsdGVyZWRbZGZfZmlsdGVyZWQkWDguZGF5LnNlYXNjYXBlcyAhPSAiNSIsIF0KCiMgUGxvdAojIFNlZSBodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvdmlyaWRpcy92aWduZXR0ZXMvaW50cm8tdG8tdmlyaWRpcy5odG1sIGZvciBjb2xvciBwYWxldHRlIG9wdGlvbnMKcHAgPC0gZGZfZmlsdGVyZWQgJT4lCiAgZ2dwbG90KCBhZXMoeD1YOC5kYXkuc2Vhc2NhcGVzLCB5PUF2Zy5jaGwuYS4udWcuTC4sIGZpbGw9WDguZGF5LnNlYXNjYXBlcykpICsKICAgIGdlb21fYm94cGxvdCgpICsKICAgICMgc2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbj0iSCIsIGRpc2NyZXRlID0gVFJVRSwgYWxwaGE9MC42KSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fY29sb3JzKSArCiAgICBnZW9tX2ppdHRlcihjb2xvcj0iZ3JleSIsIHNpemU9MC44LCBhbHBoYT0wLjQpICsKICAgIGxhYnMoeCA9ICJTZWFzY2FwZSBjbGFzcyIpICsKICAgIGxhYnMoeSA9IGV4cHJlc3Npb24oIlsiKkNobC1hKiJdIn4gbXUqImcifkxeLTEpKSArCiAgICAjIGxhYnMoeSA9IGV4cHJlc3Npb24oIlNhbGluaXR5IikpICsKICAgICMgbGFicyh5ID0gZXhwcmVzc2lvbihwYXN0ZSgiVGVtcGVyYXR1cmUgKCIsIGRlZ3JlZSwgIkMpIGF0IDEgbSBkZXB0aCIpKSkgKwogICAgIyBsYWJzKHkgPSBleHByZXNzaW9uKCJbIipETyoiXSJ+bWd+TF4tMSkpICsKICAgICMgbGFicyh5ID0gZXhwcmVzc2lvbigiWyIqTk9bIngiXSoiXSIgfiBtdSoiTSIpKSArCiAgICAjIGxhYnMoeSA9IGV4cHJlc3Npb24oIlsiKlBPWzRdXiIzLSIqIl0iIH4gbXUqIk0iKSkgKwogICAgIyBsYWJzKHkgPSBleHByZXNzaW9uKCJbIipOSFs0XV4iKyIqIl0iIH4gbXUqIk0iKSkgKwogICAgIyB0aGVtZShheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAxKSkKICAgICMgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9IGMoIlRyb3BpY2FsL1N1YnRyb3BpY2FsIFVwd2VsbGluZyIsIAogICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgIlRyb3BpY2FsIFNlYXMiLCAKICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICJXYXJtLCBCbG9vbXMsIEhpZ2ggTnV0cyIsIAogICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgIlRyb3BpY2FsL1N1YnRyb3BpY2FsIFRyYW5zaXRpb24iLCAKICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICJUZW1wZXJhdGUgVHJhbnNpdGlvbiIpKSArCiAgICB0aGVtZV9pcHN1bSgpICsKICAgIHRoZW1lKAogICAgICBsZWdlbmQucG9zaXRpb249Im5vbmUiLAogICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTEpCiAgICApICsKICAgICMgZ2d0aXRsZSgiQSBib3hwbG90IHdpdGggaml0dGVyIikgKwogICAgIyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1KSkgKwogIHlsaW0oMCwgNykgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMiwgZmFtaWx5ID0gIkFyaWFsIiksICAjIFNldCBYLWF4aXMgbGFiZWwgZm9udCBzaXplCiAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMzIsIGZhbWlseSA9ICJBcmlhbCIpKSArCiAgICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4LCBoanVzdCA9IDAuNSwgZmFtaWx5ID0gIkFyaWFsIiksCiAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4LCBoanVzdCA9IDAuNSwgZmFtaWx5ID0gIkFyaWFsIikpICsKICAgIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMikpIApwcApgYGAKCiMgQ3JlYXRlIHN0YWNrcGxvdHMgc2hvd2luZyByZWxhdGl2ZSBmcmVxdWVuY3kgdXNpbmcgY291bnRzIG9ubHkgb2YgcGxhbmt0b24gdGF4YSBwZXIgc2Vhc2NhcGUgY2F0ZWdvcnkKYGBge3J9CiMgY29udmVydCBhYnVuZGFuY2UgY29sdW1ucyB0byByZWxhdGl2ZSBmcmVxdWVuY3kKCiMgc3Vic2V0cyB0YXhhIGZvciB6b29wbGFua3RvbgojIGRmX3N1YnNldCA8LSBkZl9maWx0ZXJlZFsgLCBjKCJYOC5kYXkuc2Vhc2NhcGVzIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQWNhbnRoYXJlYSIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNvcGVwb2RzIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRWNoaW5vZGVybXMiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJKZWxsaWVzIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGFydmFjZWFucyIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBvbHljaGFldGVzIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhZXRvZ25hdGhzIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUHRlcm9wb2RzIildCgojIHN1YnNldHMgdGF4YSBmb3IgcGh5dG9wbGFua3RvbgpkZl9zdWJzZXQgPC0gZGZfZmlsdGVyZWRbICwgYygiWDguZGF5LnNlYXNjYXBlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2VyYXRpdW0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNoYWV0b2Nlcm9zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDaGFpbjIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNoYWluMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3VpbmFyZGlhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOZW9jYWx5cHRyZWxsYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJpY2hvIildCgojIHN1YnNldHMgdGF4YSBmb3IgYWxsIHNwZWNpZXMKIyBkZl9zdWJzZXQgPC0gZGZfZmlsdGVyZWRbICwgYygiWDguZGF5LnNlYXNjYXBlcyIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDZXJhdGl1bSIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDaGFldG9jZXJvcyIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEaWF0b21zIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRpYXRvbXMyIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkd1aW5hcmRpYSIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOZW9jYWx5cHRyZWxsYSIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUcmljaG9kZXNtaXVtIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkFjYW50aGFyZWEiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ29wZXBvZHMiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRWNoaW5vZGVybXMiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSmVsbGllcyIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMYXJ2YWNlYW5zIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBvbHljaGFldGVzIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNoYWV0b2duYXRocyIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQdGVyb3BvZHMiKV0KCiMgcmVzaGFwZSB0aGUgZGF0YSB0byBsb25nIGZvcm1hdCAKZGZfbG9uZyA8LSB0aWR5cjo6Z2F0aGVyKGRmX3N1YnNldCwga2V5ID0gIlNwZWNpZXMiLCB2YWx1ZSA9ICJGcmVxdWVuY3kiLCAtWDguZGF5LnNlYXNjYXBlcykKCiMgY2FsY3VsYXRlIHJlbGF0aXZlIGZyZXF1ZW5jaWVzCmRmX3N1bSA8LSBkZl9sb25nICU+JQogIGdyb3VwX2J5KFg4LmRheS5zZWFzY2FwZXMsIFNwZWNpZXMpICU+JQogIHN1bW1hcmlzZShuID0gc3VtKEZyZXF1ZW5jeSkpICU+JQogIG11dGF0ZShmcmVxID0gbiAvIHN1bShuKSkKCiMgU2Vhc2NhcGUgY2xhc3MgbmFtZXM6CiMgIkNsYXNzIDExIiAtIFRyb3BpY2FsL1N1YnRyb3BpY2FsIFVwd2VsbGluZwojICJDbGFzcyAxNSIgLSBUcm9waWNhbCBTZWFzCiMgIkNsYXNzIDIxIiAtIFdhcm0sIEJsb29tcywgSGlnaCBOdXRzCiMgIkNsYXNzIDMiIC0gVHJvcGljYWwvU3VidHJvcGljYWwgVHJhbnNpdGlvbgojICJDbGFzcyA3IiAtIFRlbXBlcmF0ZSBUcmFuc2l0aW9uCgojICMgU2VsZWN0IGRlc2lyZWQgc2Vhc2NhcGVzIGFuZCByZW9yZGVyIGNhdGVnb3JpZXMgaW4gWCBheGlzCmRmX3N1bSRYOC5kYXkuc2Vhc2NhcGVzIDwtIGZhY3RvcihkZl9zdW0kWDguZGF5LnNlYXNjYXBlcywgbGV2ZWxzID0gYygiMyIsICI1IiwgIjciLCAiMTEiLCAiMTMiLCAiMTUiLCIyMSIsIjI3IikpCgojIEZpbHRlciBvdXQgc2Vhc2NhcGUgY2xhc3MgYXMgZGVzaXJlZApleGNsdWRlX2NsYXNzZXMgPC0gYyg1LCA3LCAxMSkKZGZfc3VtIDwtIGRmX3N1bVshZGZfc3VtJFg4LmRheS5zZWFzY2FwZXMgJWluJSBleGNsdWRlX2NsYXNzZXMsIF0KCiMgVXNlIHF1YWxpdGF0aXZlIHBhbGV0dGVzOiBodHRwczovL2NvbG9yYnJld2VyMi5vcmcvI3R5cGU9cXVhbGl0YXRpdmUmc2NoZW1lPUFjY2VudCZuPTcKY3VzdG9tX3BhbF9oZXgyIDwtIGMoJyNlNDFhMWMnLCcjMzc3ZWI4JywnIzRkYWY0YScsJyM5ODRlYTMnLCcjZmY3ZjAwJywnI2ZmZmYzMycsJyNhNjU2MjgnLCcjZjc4MWJmJykKIyBjdXN0b21fcGFsX2hleDIgPC0gYygnIzFiOWU3NycsJyNkOTVmMDInLCcjNzU3MGIzJywnI2U3Mjk4YScsJyM2NmE2MWUnLCcjZTZhYjAyJywnI2E2NzYxZCcpCgojIGNyZWF0ZSB0aGUgc3RhY2twbG90CnFxIDwtIGdncGxvdChkZl9zdW0sIGFlcyh4ID0gWDguZGF5LnNlYXNjYXBlcywgeSA9IGZyZXEsIGZpbGwgPSBTcGVjaWVzKSkgKwogICMgc2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbj0iU2V0MyIsIGRpc2NyZXRlID0gVFJVRSwgYWxwaGE9MC44KSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKGRpc2NyZXRlID0gVFJVRSwgYWxwaGE9MC44KSArCiAgIyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fcGFsX2hleDIpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGxhYnMoeCA9ICJTZWFzY2FwZSBjbGFzcyIpICsKICBsYWJzKHkgPSAiUmVsYXRpdmUgZnJlcXVlbmN5IikgKwogICMgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9IGMoIlRyb3BpY2FsL1N1YnRyb3BpY2FsIFVwd2VsbGluZyIsIAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRyb3BpY2FsIFNlYXMiLCAKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICJXYXJtLCBCbG9vbXMsIEhpZ2ggTnV0cyIsIAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRyb3BpY2FsL1N1YnRyb3BpY2FsIFRyYW5zaXRpb24iLCAKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUZW1wZXJhdGUgVHJhbnNpdGlvbiIpKSArCiAgIyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1KSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwgICMgU2V0IFgtYXhpcyBsYWJlbCBmb250IHNpemUKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpKSArCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCkpICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpKSAKICAjdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoaGp1c3QgPSAxKSkKcXEKYGBgCgoKIyBDb21wdXRlIFNoYW5ub24gSW5kZXgKYGBge3J9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodmVnYW4pCgojIFNlbGVjdCBzcGVjaWVzIGZvciBTaGFubm9uIGNhbGN1bGF0aW9uCmRmX3N1YnNldF9zaGFubm9uIDwtIGRmX2ZpbHRlcmVkWyAsIGMoIlg4LmRheS5zZWFzY2FwZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQWNhbnRoYXJlYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDb3BlcG9kcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFY2hpbm9kZXJtcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJKZWxsaWVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxhcnZhY2VhbnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUG9seWNoYWV0cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDaGFldG9nbmF0aHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUHRlcm9wb2RzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNlcmF0aXVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNoYWV0b2Nlcm9zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNoYWluMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDaGFpbjMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3VpbmFyZGlhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5lb2NhbHlwdHJlbGxhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRyaWNobyIpXQoKIyByZXNoYXBlIHRoZSBkYXRhIHRvIGxvbmcgZm9ybWF0CmRmX2xvbmcgPC0gdGlkeXI6OmdhdGhlcihkZl9zdWJzZXRfc2hhbm5vbiwga2V5ID0gIlNwZWNpZXMiLCB2YWx1ZSA9ICJBYnVuZGFuY2UiLCAtWDguZGF5LnNlYXNjYXBlcykKCiMgRXhjbHVkZSBzZWFzY2FwZXMKZXhjbHVkZV9zZWFzY2FwZXMgPC0gYyg1LCA3LCAxMSkKCiMgQ29tcHV0ZSBTaGFubm9uIGRpdmVyc2l0eSBwZXIgc2Vhc2NhcGUgY2xhc3MKc2hhbm5vbl9kZiA8LSBkZl9sb25nICU+JSAKICBncm91cF9ieShYOC5kYXkuc2Vhc2NhcGVzKSAlPiUgCiAgc3VtbWFyaXNlKHNoYW5ub24gPSBkaXZlcnNpdHkoQWJ1bmRhbmNlLCBpbmRleCA9ICJzaGFubm9uIikpCgojIEZpbHRlciB0aGUgZGF0YSB0byBleGNsdWRlIHNwZWNpZmllZCBzZWFzY2FwZXMKc2hhbm5vbl9kZl9maWx0ZXJlZCA8LSBzaGFubm9uX2RmWyFzaGFubm9uX2RmJFg4LmRheS5zZWFzY2FwZXMgJWluJSBleGNsdWRlX3NlYXNjYXBlcywgXQoKIyBjcmVhdGUgdGhlIGJhciBwbG90IG9mIFNoYW5ub24gZGl2ZXJzaXR5CmZmIDwtIGdncGxvdChzaGFubm9uX2RmX2ZpbHRlcmVkLCBhZXMoeCA9IFg4LmRheS5zZWFzY2FwZXMsIHkgPSBzaGFubm9uLCBmaWxsID0gWDguZGF5LnNlYXNjYXBlcykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogICMgc2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbj0icGxhc21hIiwgZGlzY3JldGUgPSBUUlVFLCBhbHBoYT0wLjYpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fY29sb3JzKSArCiAgeGxhYigiU2Vhc2NhcGUgQ2xhc3MiKSArCiAgeWxhYigiU2hhbm5vbiBEaXZlcnNpdHkiKSArCiAgIyBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz0gYygiVHJvcGljYWwvU3VidHJvcGljYWwgVXB3ZWxsaW5nIiwgCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJvcGljYWwgU2VhcyIsIAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIldhcm0sIEJsb29tcywgSGlnaCBOdXRzIiwgCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJvcGljYWwvU3VidHJvcGljYWwgVHJhbnNpdGlvbiIsIAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRlbXBlcmF0ZSBUcmFuc2l0aW9uIikpICsKICAjIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUpKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMyKSwgICMgU2V0IFgtYXhpcyBsYWJlbCBmb250IHNpemUKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMzIpKSArCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMiksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMikpICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMzIpKSArCiAgZ3VpZGVzKCkKZmYKCiMgIyBTdWJzZXQgdGhlIGRhdGEgdG8gZXhjbHVkZSBzcGVjaWZpZWQgc2Vhc2NhcGVzCmZpbHRlcmVkX3RheGFfbWV0YSA8LSB0YXhhX21ldGFbY29tcGxldGUuY2FzZXModGF4YV9tZXRhJFg4LmRheS5zZWFzY2FwZXMpLCBdCiMgZmlsdGVyZWRfdGF4YV9tZXRhIDwtIGZpbHRlcmVkX3RheGFfbWV0YVshZmlsdGVyZWRfdGF4YV9tZXRhJFg4LmRheS5zZWFzY2FwZXMgJWluJSBleGNsdWRlX3NlYXNjYXBlcywgXQoKIyAjIFBsb3Qgc2FtcGxlZCBzZWFzY2FwZSBmcmVxdWVuY3kKdHQgPC0gZ2dwbG90KGZpbHRlcmVkX3RheGFfbWV0YSwgYWVzKHggPSBmYWN0b3IoWDguZGF5LnNlYXNjYXBlcyksIGZpbGwgPSBmYWN0b3IoWDguZGF5LnNlYXNjYXBlcykpKSArCiAgZ2VvbV9iYXIoY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuNykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9jb2xvcnMpICsKICBsYWJzKHggPSAiU2Vhc2NhcGUgQ2xhc3MiLCB5ID0gIkZyZXF1ZW5jeSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIyKSwgICMgU2V0IFgtYXhpcyBsYWJlbCBmb250IHNpemUKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjIpKSArCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMiksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMikpICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjIpKSArCiAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkKdHQKCiMgQ2FsY3VsYXRlIHBlcmNlbnQgZnJlcXVlbmN5IG9mIGVhY2ggWDguZGF5LnNlYXNjYXBlcyBjYXRlZ29yeQojIHNlZSBzZWFzY2FwZXMgY2xhc3NlcyBoZXJlOiBodHRwczovL2NvYXN0d2F0Y2gubm9hYS5nb3YvY3duL3Byb2R1Y3RzL3NlYXNjYXBlLXBlbGFnaWMtaGFiaXRhdC1jbGFzc2lmaWNhdGlvbi5odG1sCnBlcmNlbnRfZnJlcXVlbmN5IDwtIGZpbHRlcmVkX3RheGFfbWV0YSAlPiUKICBncm91cF9ieShYOC5kYXkuc2Vhc2NhcGVzKSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQogIG11dGF0ZShwZXJjZW50X2ZyZXF1ZW5jeSA9IChjb3VudCAvIHN1bShjb3VudCkpICogMTAwKQojIERpc3BsYXkgdGhlIHJlc3VsdAojIHByaW50KHBlcmNlbnRfZnJlcXVlbmN5KQoKIyAjIENvdW50cyBvZiBzcGVjaWVzIHBlciBzZWFzY2FwZQpkZl9zdW1fYWJ1bmQgPC0gZGZfbG9uZyAlPiUKICBncm91cF9ieShYOC5kYXkuc2Vhc2NhcGVzLCBTcGVjaWVzKSAlPiUKICBzdW1tYXJpc2UobiA9IHN1bShBYnVuZGFuY2UpKSAKIyBEaXNwbGF5IHRoZSByZXN1bHQKIyBwcmludChkZl9zdW1fYWJ1bmQpCgpkZl9zdW1fYWJ1bmRfZmlsdCA8LSBkZl9zdW1fYWJ1bmRbZGZfc3VtX2FidW5kJFNwZWNpZXMgPT0gIkxhcnZhY2VhbnMiLF0KIyBGaWx0ZXIgdGhlIGRhdGEgdG8gZXhjbHVkZSBzcGVjaWZpZWQgc2Vhc2NhcGVzCmRmX3N1bV9hYnVuZF9maWx0MiA8LSBkZl9zdW1fYWJ1bmRfZmlsdFshZGZfc3VtX2FidW5kX2ZpbHQkWDguZGF5LnNlYXNjYXBlcyAlaW4lIGV4Y2x1ZGVfc2Vhc2NhcGVzLCBdCgpkZCA8LSBnZ3Bsb3QoZGZfc3VtX2FidW5kX2ZpbHQyLCBhZXMoeCA9IGZhY3RvcihYOC5kYXkuc2Vhc2NhcGVzKSwgeSA9IG4sIGZpbGwgPSBmYWN0b3IoWDguZGF5LnNlYXNjYXBlcykpKSArCiAgZ2VvbV9iYXIoY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuNywgc3RhdCA9ICJpZGVudGl0eSIpICsKICAjIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAibWFnbWEiLCBkaXNjcmV0ZSA9IFRSVUUsIGFscGhhID0gMC44KQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9jb2xvcnMpICsKICB0aGVtZV9taW5pbWFsKCkgCmRkCgojIENvbXB1dGUgY291bnRzIG9mIHNlbGVjdGVkIHNwZWNpZXMgcGVyIHNlYXNjYXBlIGNsYXNzIG5vcm1hbGl6ZWQgYnkgZnJlcXVlbmN5IG9mIHNlYXNjYXBlcwojIHJlc2hhcGUgdGhlIGRhdGEgdG8gbG9uZyBmb3JtYXQKZGZfbG9uZzIgPC0gdGlkeXI6OmdhdGhlcihkZl9zdWJzZXRfc2hhbm5vbiwga2V5ID0gIlNwZWNpZXMiLCB2YWx1ZSA9ICJBYnVuZGFuY2UiLCAtWDguZGF5LnNlYXNjYXBlcykKCnNwcF9zZWFzY2FwZV9ub3JtYWxpemVkIDwtIGRmX2xvbmcyICU+JQogIGZpbHRlcihTcGVjaWVzID09ICJMYXJ2YWNlYW5zIikgJT4lCiAgZ3JvdXBfYnkoWDguZGF5LnNlYXNjYXBlcykgJT4lCiAgc3VtbWFyaXNlKHNwcF9jb3VudCA9IHN1bShBYnVuZGFuY2UpKQoKIyBDb21wdXRlIGZyZXF1ZW5jaWVzIG9mIGVhY2ggc2Vhc2NhcGUgY2xhc3MKc2Vhc2NhcGVfZnJlcXVlbmNpZXMgPC0gZGZfbG9uZzIgJT4lCiAgY291bnQoWDguZGF5LnNlYXNjYXBlcykgJT4lCiAgcmVuYW1lKGZyZXEgPSBuKQoKIyBNZXJnZSBjb3VudHMgd2l0aCBmcmVxdWVuY2llcwpzcHBfc2Vhc2NhcGVfbm9ybWFsaXplZCA8LSBtZXJnZShzcHBfc2Vhc2NhcGVfbm9ybWFsaXplZCwgc2Vhc2NhcGVfZnJlcXVlbmNpZXMsIGJ5ID0gIlg4LmRheS5zZWFzY2FwZXMiKQoKIyBOb3JtYWxpemUgY291bnRzIGJ5IGZyZXF1ZW5jeQpzcHBfc2Vhc2NhcGVfbm9ybWFsaXplZCRzcHBfbm9ybWFsaXplZCA8LSAoc3BwX3NlYXNjYXBlX25vcm1hbGl6ZWQkc3BwX2NvdW50IC8gc3BwX3NlYXNjYXBlX25vcm1hbGl6ZWQkZnJlcSkgKiAxMDAwCnNwcF9zZWFzY2FwZV9ub3JtYWxpemVkX2ZpbHQgPC0gc3BwX3NlYXNjYXBlX25vcm1hbGl6ZWRbIXNwcF9zZWFzY2FwZV9ub3JtYWxpemVkJFg4LmRheS5zZWFzY2FwZXMgJWluJSBleGNsdWRlX3NlYXNjYXBlcywgXQoKYmIgPC0gZ2dwbG90KHNwcF9zZWFzY2FwZV9ub3JtYWxpemVkX2ZpbHQsIGFlcyh4ID0gZmFjdG9yKFg4LmRheS5zZWFzY2FwZXMpLCB5ID0gc3BwX25vcm1hbGl6ZWQsIGZpbGwgPSBmYWN0b3IoWDguZGF5LnNlYXNjYXBlcykpKSArCiAgZ2VvbV9iYXIoY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuNywgc3RhdCA9ICJpZGVudGl0eSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fY29sb3JzKSArCiAgbGFicyh4ID0gIlNlYXNjYXBlIENsYXNzIiwgeSA9ICJDb3VudHMgLyBTZWFzY2FwZSBmcmVxICogMTAwMCIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogICMgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMzIpLCAgIyBTZXQgWC1heGlzIGxhYmVsIGZvbnQgc2l6ZQogICMgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMyKSkgKwogICMgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMiksCiAgIyAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMyKSkgKwogICMgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMyKSkgKwogIGd1aWRlcygpCmJiCgpgYGAKCiMgUEMgcGxvdApgYGB7cn0KbGlicmFyeShnZ2FsdCkKCiMgUGVyZm9ybSBwcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzIG9uIHRoZSBjb3VudCBkYXRhCgojIGZvciBoeWRyb2dyYXBoeQpzZWxfdmFycyA8LSBjKAogICJYOC5kYXkuc2Vhc2NhcGVzIiwKICAic2FsaW5pdHkiLAogICJBdmcuY2hsLmEuLnVnLkwuIiwKICAiUE80Li4udU0uIiwKICAiTk8zLk5PMi4udU0uIiwKICAiTkg0Li4udU0uIiwKICAidGVtcC4uZGVnQy4iKQoKIyBmb3IgdGF4b25vbXkKIyBzZWxfdmFycyA8LSBjKCJYOC5kYXkuc2Vhc2NhcGVzIiwKIyAgICAgICAgICAgICAgICJBY2FudGhhcmVhIiwKIyAgICAgICAgICAgICAgICJDb3BlcG9kcyIsCiMgICAgICAgICAgICAgICAiRWNoaW5vZGVybXMiLAojICAgICAgICAgICAgICAgIkplbGxpZXMiLAojICAgICAgICAgICAgICAgIkxhcnZhY2VhbnMiLAojICAgICAgICAgICAgICAgIlBvbHljaGFldHMiLAojICAgICAgICAgICAgICAgIkNoYWV0b2duYXRocyIsCiMgICAgICAgICAgICAgICAiUHRlcm9wb2RzIikKCiMgc2VsX3ZhcnMgPC0gYygiWDguZGF5LnNlYXNjYXBlcyIsCiMgICAgICAgICAgICAgICAiQ2VyYXRpdW0iLAojICAgICAgICAgICAgICAgIkNoYWV0b2Nlcm9zIiwKIyAgICAgICAgICAgICAgICJDaGFpbjIiLAojICAgICAgICAgICAgICAgIkNoYWluMyIsCiMgICAgICAgICAgICAgICAiR3VpbmFyZGlhIiwKIyAgICAgICAgICAgICAgICJOZW9jYWx5cHRyZWxsYSIsCiMgICAgICAgICAgICAgICAiVHJpY2hvIikKCiMgc2VsX3ZhcnMgPC0gYygiWDguZGF5LnNlYXNjYXBlcyIsCiMgICAgICAgICAgICAgICAiQWNhbnRoYXJlYSIsCiMgICAgICAgICAgICAgICAiQ29wZXBvZHMiLAojICAgICAgICAgICAgICAgIkVjaGlub2Rlcm1zIiwKIyAgICAgICAgICAgICAgICJKZWxsaWVzIiwKIyAgICAgICAgICAgICAgICJMYXJ2YWNlYW5zIiwKIyAgICAgICAgICAgICAgICJQb2x5Y2hhZXRzIiwKIyAgICAgICAgICAgICAgICJDaGFldG9nbmF0aHMiLAojICAgICAgICAgICAgICAgIlB0ZXJvcG9kcyIsCiMgICAgICAgICAgICAgICAiQ2VyYXRpdW0iLAojICAgICAgICAgICAgICAgIkNoYWV0b2Nlcm9zIiwKIyAgICAgICAgICAgICAgICJDaGFpbjIiLAojICAgICAgICAgICAgICAgIkNoYWluMyIsCiMgICAgICAgICAgICAgICAiR3VpbmFyZGlhIiwKIyAgICAgICAgICAgICAgICJOZW9jYWx5cHRyZWxsYSIsCiMgICAgICAgICAgICAgICAiVHJpY2hvIikKCiMgZmlsdGVyIG91dCByb3dzIHdpdGggTkEgdmFsdWVzIGluIGNvbHVtbiBzZWFzY2FwZXMKIyBkZl9maWx0ZXJlZF9wY2EgPC0gdGF4YV9tZXRhW2NvbXBsZXRlLmNhc2VzKHRheGFfbWV0YSRYOC5kYXkuc2Vhc2NhcGVzKSwgXQpkZl9maWx0ZXJlZF9wY2EgPC0gdGF4YV9tZXRhW2NvbXBsZXRlLmNhc2VzKHRheGFfbWV0YVssIHNlbF92YXJzXSksIF0KIyBleGNsdWRlX3NlYXNjYXBlcyA8LSBjKDUsIDcsIDExKQpleGNsdWRlX3NlYXNjYXBlcyA8LSAwCmZpbHRfZGZfcGNhIDwtIGRmX2ZpbHRlcmVkX3BjYVshZGZfZmlsdGVyZWRfcGNhJFg4LmRheS5zZWFzY2FwZXMgJWluJQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleGNsdWRlX3NlYXNjYXBlcywgc2VsX3ZhcnNdCgojIFNlbGVjdCBudW1lcmljIGNvbHVtbnMgaW4gZmlsdF9kZl9wY2EKbnVtZXJpY19jb2xzIDwtIHNhcHBseShmaWx0X2RmX3BjYSwgaXMubnVtZXJpYykKIyBJZGVudGlmeSBudW1lcmljIGNvbHVtbnMgZXhjZXB0IHRoZSBmaXJzdCBvbmUKbnVtZXJpY19jb2xzIDwtIDI6bmNvbChmaWx0X2RmX3BjYSkKCiMgVHJhbnNmb3JtIG51bWVyaWMgY29sdW1ucyB0byBsb2cgc2NhbGUKZmlsdF9kZl9wY2FbbnVtZXJpY19jb2xzXSA8LSBsYXBwbHkoZmlsdF9kZl9wY2FbbnVtZXJpY19jb2xzXSwgZnVuY3Rpb24oeCkgbG9nKHggKyAxKSkKCgojIHBjYSA8LSBwcmNvbXAoZmlsdF9kZl9wY2FbLCBjKCJzYWxpbml0eSIsICJBdmcuY2hsLmEuLnVnLkwuIiwgIlBPNC4uLnVNLiIsICJOTzMuTk8yLi51TS4iKV0sIHNjYWxlLiA9IFRSVUUpCnBjYSA8LSBwcmNvbXAoZmlsdF9kZl9wY2FbLCAtMV0sIHNjYWxlLiA9IFRSVUUpCgojIEV4dHJhY3QgUEMxIGFuZCBQQzIgc2NvcmVzIGZvciBlYWNoIHNhbXBsaW5nIGV2ZW50CiMgcGNfc2NvcmVzIDwtIGRhdGEuZnJhbWUoc2Vhc2NhcGUgPSBkZl9zdWJzZXQkWDguZGF5LnNlYXNjYXBlcywgIyBmb3IgdGF4b25vbWljIGFuYWx5c2lzIApwY19zY29yZXMgPC0gZGF0YS5mcmFtZShzZWFzY2FwZSA9IGFzLmNoYXJhY3RlcihmaWx0X2RmX3BjYSRYOC5kYXkuc2Vhc2NhcGVzKSwgIyBmb3IgaHlkcm9ncmFwaHkKICAgICAgICAgICAgICAgICAgICAgICAgUEMxID0gcGNhJHhbLCAxXSwgCiAgICAgICAgICAgICAgICAgICAgICAgIFBDMiA9IHBjYSR4WywgMl0pCgojIENyZWF0ZSB0aGUgcGxvdApwY19zY29yZXMkc2Vhc2NhcGUgPC0gZmFjdG9yKHBjX3Njb3JlcyRzZWFzY2FwZSwgbGV2ZWxzID0gYygiMyIsICI1IiwgIjciLCAiMTEiLCAiMTMiLCAiMTUiLCAiMjEiLCAiMjciKSkKYmIgPC0gZ2dwbG90KHBjX3Njb3JlcywgYWVzKHggPSBQQzEsIHkgPSBQQzIsIGNvbG9yID0gc2Vhc2NhcGUpKSArIAogIGdlb21fcG9pbnQoKSArCiAgbGFicyh4ID0gIlBDMSIsIHkgPSAiUEMyIiwgY29sb3IgPSAiU2Vhc2NhcGUiKSAKCiMgYWRkIGNpcmNsZSBhcm91bmQgY2x1c3RlciBvZiBkYXRhIHBvaW50cwojIGN1c3RvbV9jb2xvcnNfcGNhIDwtIGN1c3RvbV9wYWxfaGV4W2MoMSwgNSwgNiwgNywgOCldCmN1c3RvbV9jb2xvcnNfcGNhIDwtIGN1c3RvbV9jb2xvcnMKCnl5IDwtIGdncGxvdChwY19zY29yZXMsIGFlcyh4ID0gUEMxLCB5ID0gUEMyLCBjb2xvciA9IHNlYXNjYXBlKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIHN0YXRfZWxsaXBzZShhZXMoZmlsbCA9IHNlYXNjYXBlKSwgbGV2ZWwgPSAwLjkwLCBnZW9tID0gInBvbHlnb24iLCBhbHBoYSA9IDAuMywgY29sb3IgPSAiYmxhY2siKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9jb2xvcnNfcGNhKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY3VzdG9tX2NvbG9yc19wY2EpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHhsaW0oLTIsMikgKwogIHlsaW0oLTIsMikgKwogICMgeGxpbSgtMSwxKSArCiAgIyB5bGltKC0xLDEpICsKICBnZW9tX3BvaW50KHNpemU9MikgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPTIpKSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMzIpLCAgIyBTZXQgWC1heGlzIGxhYmVsIGZvbnQgc2l6ZQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMikpICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMyKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMyKSkgKwogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMikpCgp5eQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgIyBDcmVhdGUgUENBIHdpdGggZWlnZW52ZWN0b3JzCiMgRXh0cmFjdCBwcmluY2lwYWwgY29tcG9uZW50IHNjb3JlcwpwY19zY29yZXMyIDwtIHBjYSR4CiMgRXh0cmFjdCBlaWdlbnZlY3RvcnMKZWlnZW52ZWN0b3JzIDwtIHBjYSRyb3RhdGlvbgojIENhbGN1bGF0ZSB0aGUgcGVyY2VudGFnZSB2YXJpYW5jZSBleHBsYWluZWQgYnkgZWFjaCBwcmluY2lwYWwgY29tcG9uZW50CnRvdGFsX3ZhcmlhbmNlIDwtIHN1bShwY2Ekc2Rldl4yKQpwY192YXJfcGVyY2VudCA8LSByb3VuZCgxMDAgKiAocGNhJHNkZXZeMikgLyB0b3RhbF92YXJpYW5jZSwgMSkKCiMgQ29udmVydCBYOC5kYXkuc2Vhc2NhcGVzIHRvIGEgZmFjdG9yCmZpbHRfZGZfcGNhJFg4LmRheS5zZWFzY2FwZXMgPC0gYXMuZmFjdG9yKGZpbHRfZGZfcGNhJFg4LmRheS5zZWFzY2FwZXMpCgojICMgRm9yIGh5ZHJvZ3JhcGh5CnFxIDwtIGdncGxvdChmaWx0X2RmX3BjYSwgYWVzKHggPSBwY19zY29yZXMyWywxXSwgeSA9IHBjX3Njb3JlczJbLDJdLCBjb2xvciA9IFg4LmRheS5zZWFzY2FwZXMpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMywgYWxwaGEgPSAwLjcpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY3VzdG9tX2NvbG9yc19wY2EpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IGVpZ2VudmVjdG9yc1sxLCAxXSwgeWVuZCA9IGVpZ2VudmVjdG9yc1syLCAxXSksCiAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjEsICJpbmNoZXMiKSksIGNvbG9yID0gImJsYWNrIikgKyAgIyBBZGQgdmVjdG9yIGZvciBQQzEKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IGVpZ2VudmVjdG9yc1sxLCAyXSwgeWVuZCA9IGVpZ2VudmVjdG9yc1syLCAyXSksCiAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjEsICJpbmNoZXMiKSksIGNvbG9yID0gImJsYWNrIikgKyAjIEFkZCB2ZWN0b3IgZm9yIFBDMgogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gZWlnZW52ZWN0b3JzWzEsIDNdLCB5ZW5kID0gZWlnZW52ZWN0b3JzWzIsIDNdKSwKICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMSwgImluY2hlcyIpKSwgY29sb3IgPSAiYmxhY2siKSArICMgQWRkIHZlY3RvciBmb3IgUEMzCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeSA9IDAsIHhlbmQgPSBlaWdlbnZlY3RvcnNbMSwgNF0sIHllbmQgPSBlaWdlbnZlY3RvcnNbMiwgNF0pLAogICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4xLCAiaW5jaGVzIikpLCBjb2xvciA9ICJibGFjayIpICsgIyBBZGQgdmVjdG9yIGZvciBQQzQKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IGVpZ2VudmVjdG9yc1sxLCA1XSwgeWVuZCA9IGVpZ2VudmVjdG9yc1syLCA1XSksCiAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjEsICJpbmNoZXMiKSksIGNvbG9yID0gImJsYWNrIikgKyAjIEFkZCB2ZWN0b3IgZm9yIFBDNQogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gZWlnZW52ZWN0b3JzWzEsIDZdLCB5ZW5kID0gZWlnZW52ZWN0b3JzWzIsIDZdKSwKICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMSwgImluY2hlcyIpKSwgY29sb3IgPSAiYmxhY2siKSArICMgQWRkIHZlY3RvciBmb3IgUEM2CiAgZ2VvbV90ZXh0KGFlcyh4ID0gZWlnZW52ZWN0b3JzWzEsIDFdLCB5ID0gZWlnZW52ZWN0b3JzWzIsIDFdLCAKICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlBDMSAoIiwgcGNfdmFyX3BlcmNlbnRbMV0sICIlOiBTYWxpbml0eSkiLCBzZXAgPSAiIikpLAogICAgICAgICAgICB2anVzdCA9IC0wLjUsIGhqdXN0ID0gMC41LCBjb2xvciA9ICJibGFjayIpICsgICMgQWRkIGxhYmVsIGZvciBQQzEKICBnZW9tX3RleHQoYWVzKHggPSBlaWdlbnZlY3RvcnNbMSwgMl0sIHkgPSBlaWdlbnZlY3RvcnNbMiwgMl0sIAogICAgICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiUEMyICgiLCBwY192YXJfcGVyY2VudFsyXSwgIiU6IENobGEpIiwgc2VwID0gIiIpKSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBoanVzdCA9IDAuNSwgY29sb3IgPSAiYmxhY2siKSArICAjIEFkZCBsYWJlbCBmb3IgUEMyCiAgZ2VvbV90ZXh0KGFlcyh4ID0gZWlnZW52ZWN0b3JzWzEsIDNdLCB5ID0gZWlnZW52ZWN0b3JzWzIsIDNdLCAKICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlBDMyAoIiwgcGNfdmFyX3BlcmNlbnRbM10sICIlOiBQaG9zcGhhdGUpIiwgc2VwID0gIiIpKSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBoanVzdCA9IDAuNSwgY29sb3IgPSAiYmxhY2siKSArICAjIEFkZCBsYWJlbCBmb3IgUEMzCiAgZ2VvbV90ZXh0KGFlcyh4ID0gZWlnZW52ZWN0b3JzWzEsIDRdLCB5ID0gZWlnZW52ZWN0b3JzWzIsIDRdLCAKICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlBDNCAoIiwgcGNfdmFyX3BlcmNlbnRbNF0sICIlOiBOT3gpIiwgc2VwID0gIiIpKSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBoanVzdCA9IDAuNSwgY29sb3IgPSAiYmxhY2siKSArICAjIEFkZCBsYWJlbCBmb3IgUEM0CiAgZ2VvbV90ZXh0KGFlcyh4ID0gZWlnZW52ZWN0b3JzWzEsIDVdLCB5ID0gZWlnZW52ZWN0b3JzWzIsIDVdLCAKICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlBDNSAoIiwgcGNfdmFyX3BlcmNlbnRbNV0sICIlOiBBbW1vbml1bSkiLCBzZXAgPSAiIikpLAogICAgICAgICAgICB2anVzdCA9IC0wLjUsIGhqdXN0ID0gMC41LCBjb2xvciA9ICJibGFjayIpICsgICMgQWRkIGxhYmVsIGZvciBQQzUKICBnZW9tX3RleHQoYWVzKHggPSBlaWdlbnZlY3RvcnNbMSwgNl0sIHkgPSBlaWdlbnZlY3RvcnNbMiwgNl0sIAogICAgICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiUEM2ICgiLCBwY192YXJfcGVyY2VudFs2XSwgIiU6IFRlbXBlcmF0dXJlKSIsIHNlcCA9ICIiKSksCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwgaGp1c3QgPSAwLjUsIGNvbG9yID0gImJsYWNrIikgKyAgIyBBZGQgbGFiZWwgZm9yIFBDNgogIGxhYnMoeCA9ICJQQzEiLCB5ID0gIlBDMiIsIGNvbG9yID0gIlNlYXNjYXBlIGNsYXNzIikgKwogIHhsaW0oLTEuNSwgMS41KSArCiAgeWxpbSgtMS41LCAxLjUpICsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT01KSkpICsgCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpLCAgIyBTZXQgWC1heGlzIGxhYmVsIGZvbnQgc2l6ZQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCkpICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkgKwogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkKcXEKCgojICMgIyBGb3IgcGh5dG9wbGFua3RvbgojIHFxMiA8LSBnZ3Bsb3QoZmlsdF9kZl9wY2EsIGFlcyh4ID0gcGNfc2NvcmVzMlssMV0sIHkgPSBwY19zY29yZXMyWywzXSwgY29sb3IgPSBYOC5kYXkuc2Vhc2NhcGVzKSkgKwojICAgZ2VvbV9wb2ludChzaXplID0gNCkgKwojICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9jb2xvcnNfcGNhKSArCiMgICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IGVpZ2VudmVjdG9yc1sxLCAxXSwgeWVuZCA9IGVpZ2VudmVjdG9yc1syLCAxXSksCiMgICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMiwgImluY2hlcyIpKSwgY29sb3IgPSAiYmxhY2siKSArICAjIEFkZCB2ZWN0b3IgZm9yIFBDMQojICAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeSA9IDAsIHhlbmQgPSBlaWdlbnZlY3RvcnNbMSwgMl0sIHllbmQgPSBlaWdlbnZlY3RvcnNbMiwgMl0pLAojICAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjIsICJpbmNoZXMiKSksIGNvbG9yID0gImJsYWNrIikgKyAjIEFkZCB2ZWN0b3IgZm9yIFBDMgojICAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeSA9IDAsIHhlbmQgPSBlaWdlbnZlY3RvcnNbMSwgM10sIHllbmQgPSBlaWdlbnZlY3RvcnNbMiwgM10pLAojICAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjIsICJpbmNoZXMiKSksIGNvbG9yID0gImJsYWNrIikgKyAjIEFkZCB2ZWN0b3IgZm9yIFBDMwojICAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeSA9IDAsIHhlbmQgPSBlaWdlbnZlY3RvcnNbMSwgNF0sIHllbmQgPSBlaWdlbnZlY3RvcnNbMiwgNF0pLAojICAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjIsICJpbmNoZXMiKSksIGNvbG9yID0gImJsYWNrIikgKyAjIEFkZCB2ZWN0b3IgZm9yIFBDNAojICAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeSA9IDAsIHhlbmQgPSBlaWdlbnZlY3RvcnNbMSwgNV0sIHllbmQgPSBlaWdlbnZlY3RvcnNbMiwgNV0pLAojICAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjIsICJpbmNoZXMiKSksIGNvbG9yID0gImJsYWNrIikgKyAjIEFkZCB2ZWN0b3IgZm9yIFBDNQojICAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeSA9IDAsIHhlbmQgPSBlaWdlbnZlY3RvcnNbMSwgNl0sIHllbmQgPSBlaWdlbnZlY3RvcnNbMiwgNl0pLAojICAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjIsICJpbmNoZXMiKSksIGNvbG9yID0gImJsYWNrIikgKyAjIEFkZCB2ZWN0b3IgZm9yIFBDNgojICAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeSA9IDAsIHhlbmQgPSBlaWdlbnZlY3RvcnNbMSwgN10sIHllbmQgPSBlaWdlbnZlY3RvcnNbMiwgN10pLAojICAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjIsICJpbmNoZXMiKSksIGNvbG9yID0gImJsYWNrIikgKyAjIEFkZCB2ZWN0b3IgZm9yIFBDNwojICAgZ2VvbV90ZXh0KGFlcyh4ID0gZWlnZW52ZWN0b3JzWzEsIDFdLCB5ID0gZWlnZW52ZWN0b3JzWzIsIDFdLCAKIyAgICAgICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiUEMxICgiLCBwY192YXJfcGVyY2VudFsxXSwgIiU6IENlcmF0aXVtKSIsIHNlcCA9ICIiKSksCiMgICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBoanVzdCA9IDAuNSwgY29sb3IgPSAiYmxhY2siKSArICAjIEFkZCBsYWJlbCBmb3IgUEMxCiMgICBnZW9tX3RleHQoYWVzKHggPSBlaWdlbnZlY3RvcnNbMSwgMl0sIHkgPSBlaWdlbnZlY3RvcnNbMiwgMl0sIAojICAgICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJQQzIgKCIsIHBjX3Zhcl9wZXJjZW50WzJdLCAiJTogQ2hhZXRvY2Vyb3MpIiwgc2VwID0gIiIpKSwKIyAgICAgICAgICAgICB2anVzdCA9IC0wLjUsIGhqdXN0ID0gMC41LCBjb2xvciA9ICJibGFjayIpICsgICMgQWRkIGxhYmVsIGZvciBQQzIKIyAgIGdlb21fdGV4dChhZXMoeCA9IGVpZ2VudmVjdG9yc1sxLCAzXSwgeSA9IGVpZ2VudmVjdG9yc1syLCAzXSwgCiMgICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlBDMyAoIiwgcGNfdmFyX3BlcmNlbnRbM10sICIlOiBEaWF0b21zKSIsIHNlcCA9ICIiKSksCiMgICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBoanVzdCA9IDAuNSwgY29sb3IgPSAiYmxhY2siKSArICAjIEFkZCBsYWJlbCBmb3IgUEMzCiMgICBnZW9tX3RleHQoYWVzKHggPSBlaWdlbnZlY3RvcnNbMSwgNF0sIHkgPSBlaWdlbnZlY3RvcnNbMiwgNF0sIAojICAgICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJQQzQgKCIsIHBjX3Zhcl9wZXJjZW50WzRdLCAiJTogRGlhdG9tczIpIiwgc2VwID0gIiIpKSwKIyAgICAgICAgICAgICB2anVzdCA9IC0wLjUsIGhqdXN0ID0gMC41LCBjb2xvciA9ICJibGFjayIpICsgICMgQWRkIGxhYmVsIGZvciBQQzQKIyAgIGdlb21fdGV4dChhZXMoeCA9IGVpZ2VudmVjdG9yc1sxLCA1XSwgeSA9IGVpZ2VudmVjdG9yc1syLCA1XSwgCiMgICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlBDNSAoIiwgcGNfdmFyX3BlcmNlbnRbNV0sICIlOiBHdWluYXJkaWEpIiwgc2VwID0gIiIpKSwKIyAgICAgICAgICAgICB2anVzdCA9IC0wLjUsIGhqdXN0ID0gMC41LCBjb2xvciA9ICJibGFjayIpICsgICMgQWRkIGxhYmVsIGZvciBQQzUKIyAgIGdlb21fdGV4dChhZXMoeCA9IGVpZ2VudmVjdG9yc1sxLCA2XSwgeSA9IGVpZ2VudmVjdG9yc1syLCA2XSwgCiMgICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlBDNiAoIiwgcGNfdmFyX3BlcmNlbnRbNl0sICIlOiBOZW9jYWx5cHRyZWxsYSkiLCBzZXAgPSAiIikpLAojICAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwgaGp1c3QgPSAwLjUsIGNvbG9yID0gImJsYWNrIikgKyAgIyBBZGQgbGFiZWwgZm9yIFBDNgojICAgZ2VvbV90ZXh0KGFlcyh4ID0gZWlnZW52ZWN0b3JzWzEsIDddLCB5ID0gZWlnZW52ZWN0b3JzWzIsIDddLCAKIyAgICAgICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiUEM3ICgiLCBwY192YXJfcGVyY2VudFs3XSwgIiU6IFRyaWNob2Rlc21pdW0pIiwgc2VwID0gIiIpKSwKIyAgICAgICAgICAgICB2anVzdCA9IC0wLjUsIGhqdXN0ID0gMC41LCBjb2xvciA9ICJibGFjayIpICsgICMgQWRkIGxhYmVsIGZvciBQQzcKIyAgIGxhYnMoeCA9ICJQQzEiLCB5ID0gIlBDMyIsIGNvbG9yID0gIlg4LmRheS5zZWFzY2FwZXMiKSArCiMgICB4bGltKC0xLCAxKSArCiMgICB5bGltKC0xLCAxKSArCiMgICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0yKSkpICsgCiMgICB0aGVtZV9jbGFzc2ljKCkgKwojICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMyKSwgICMgU2V0IFgtYXhpcyBsYWJlbCBmb250IHNpemUKIyAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMikpICsKIyAgIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMzIpLAojICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMikpICsKIyAgIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMikpIAojIHFxMgojIAojIAojICMgIyBGb3Igem9vcGxhbmt0b24KIyBxcTMgPC0gZ2dwbG90KGZpbHRfZGZfcGNhLCBhZXMoeCA9IHBjX3Njb3JlczJbLDFdLCB5ID0gcGNfc2NvcmVzMlssMl0sIGNvbG9yID0gWDguZGF5LnNlYXNjYXBlcykpICsKIyAgIGdlb21fcG9pbnQoc2l6ZSA9IDQpICsKIyAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fY29sb3JzX3BjYSkgKwojICAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeSA9IDAsIHhlbmQgPSBlaWdlbnZlY3RvcnNbMSwgMV0sIHllbmQgPSBlaWdlbnZlY3RvcnNbMiwgMV0pLAojICAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjIsICJpbmNoZXMiKSksIGNvbG9yID0gImJsYWNrIikgKyAgIyBBZGQgdmVjdG9yIGZvciBQQzEKIyAgIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gZWlnZW52ZWN0b3JzWzEsIDJdLCB5ZW5kID0gZWlnZW52ZWN0b3JzWzIsIDJdKSwKIyAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiaW5jaGVzIikpLCBjb2xvciA9ICJibGFjayIpICsgIyBBZGQgdmVjdG9yIGZvciBQQzIKIyAgIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gZWlnZW52ZWN0b3JzWzEsIDNdLCB5ZW5kID0gZWlnZW52ZWN0b3JzWzIsIDNdKSwKIyAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiaW5jaGVzIikpLCBjb2xvciA9ICJibGFjayIpICsgIyBBZGQgdmVjdG9yIGZvciBQQzMKIyAgIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gZWlnZW52ZWN0b3JzWzEsIDRdLCB5ZW5kID0gZWlnZW52ZWN0b3JzWzIsIDRdKSwKIyAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiaW5jaGVzIikpLCBjb2xvciA9ICJibGFjayIpICsgIyBBZGQgdmVjdG9yIGZvciBQQzQKIyAgIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gZWlnZW52ZWN0b3JzWzEsIDVdLCB5ZW5kID0gZWlnZW52ZWN0b3JzWzIsIDVdKSwKIyAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiaW5jaGVzIikpLCBjb2xvciA9ICJibGFjayIpICsgIyBBZGQgdmVjdG9yIGZvciBQQzUKIyAgIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gZWlnZW52ZWN0b3JzWzEsIDZdLCB5ZW5kID0gZWlnZW52ZWN0b3JzWzIsIDZdKSwKIyAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiaW5jaGVzIikpLCBjb2xvciA9ICJibGFjayIpICsgIyBBZGQgdmVjdG9yIGZvciBQQzYKIyAgIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gZWlnZW52ZWN0b3JzWzEsIDddLCB5ZW5kID0gZWlnZW52ZWN0b3JzWzIsIDddKSwKIyAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiaW5jaGVzIikpLCBjb2xvciA9ICJibGFjayIpICsgIyBBZGQgdmVjdG9yIGZvciBQQzcKIyAgIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gZWlnZW52ZWN0b3JzWzEsIDhdLCB5ZW5kID0gZWlnZW52ZWN0b3JzWzIsIDhdKSwKIyAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiaW5jaGVzIikpLCBjb2xvciA9ICJibGFjayIpICsgIyBBZGQgdmVjdG9yIGZvciBQQzgKIyAgIGdlb21fdGV4dChhZXMoeCA9IGVpZ2VudmVjdG9yc1sxLCAxXSwgeSA9IGVpZ2VudmVjdG9yc1syLCAxXSwgCiMgICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlBDMSAoIiwgcGNfdmFyX3BlcmNlbnRbMV0sICIlOiBBY2FudGhhcmVhKSIsIHNlcCA9ICIiKSksCiMgICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBoanVzdCA9IDAuNSwgY29sb3IgPSAiYmxhY2siKSArICAjIEFkZCBsYWJlbCBmb3IgUEMxCiMgICBnZW9tX3RleHQoYWVzKHggPSBlaWdlbnZlY3RvcnNbMSwgMl0sIHkgPSBlaWdlbnZlY3RvcnNbMiwgMl0sIAojICAgICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJQQzIgKCIsIHBjX3Zhcl9wZXJjZW50WzJdLCAiJTogQ29wZXBvZHMpIiwgc2VwID0gIiIpKSwKIyAgICAgICAgICAgICB2anVzdCA9IC0wLjUsIGhqdXN0ID0gMC41LCBjb2xvciA9ICJibGFjayIpICsgICMgQWRkIGxhYmVsIGZvciBQQzIKIyAgIGdlb21fdGV4dChhZXMoeCA9IGVpZ2VudmVjdG9yc1sxLCAzXSwgeSA9IGVpZ2VudmVjdG9yc1syLCAzXSwgCiMgICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlBDMyAoIiwgcGNfdmFyX3BlcmNlbnRbM10sICIlOiBFY2hpbm9kZXJtcykiLCBzZXAgPSAiIikpLAojICAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwgaGp1c3QgPSAwLjUsIGNvbG9yID0gImJsYWNrIikgKyAgIyBBZGQgbGFiZWwgZm9yIFBDMwojICAgZ2VvbV90ZXh0KGFlcyh4ID0gZWlnZW52ZWN0b3JzWzEsIDRdLCB5ID0gZWlnZW52ZWN0b3JzWzIsIDRdLCAKIyAgICAgICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiUEM0ICgiLCBwY192YXJfcGVyY2VudFs0XSwgIiU6IEplbGxpZXMpIiwgc2VwID0gIiIpKSwKIyAgICAgICAgICAgICB2anVzdCA9IC0wLjUsIGhqdXN0ID0gMC41LCBjb2xvciA9ICJibGFjayIpICsgICMgQWRkIGxhYmVsIGZvciBQQzQKIyAgIGdlb21fdGV4dChhZXMoeCA9IGVpZ2VudmVjdG9yc1sxLCA1XSwgeSA9IGVpZ2VudmVjdG9yc1syLCA1XSwgCiMgICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlBDNSAoIiwgcGNfdmFyX3BlcmNlbnRbNV0sICIlOiBMYXJ2YWNlYW5zKSIsIHNlcCA9ICIiKSksCiMgICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBoanVzdCA9IDAuNSwgY29sb3IgPSAiYmxhY2siKSArICAjIEFkZCBsYWJlbCBmb3IgUEM1CiMgICBnZW9tX3RleHQoYWVzKHggPSBlaWdlbnZlY3RvcnNbMSwgNl0sIHkgPSBlaWdlbnZlY3RvcnNbMiwgNl0sIAojICAgICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJQQzYgKCIsIHBjX3Zhcl9wZXJjZW50WzZdLCAiJTogUG9seWNoYWV0ZXMpIiwgc2VwID0gIiIpKSwKIyAgICAgICAgICAgICB2anVzdCA9IC0wLjUsIGhqdXN0ID0gMC41LCBjb2xvciA9ICJibGFjayIpICsgICMgQWRkIGxhYmVsIGZvciBQQzYKIyAgIGdlb21fdGV4dChhZXMoeCA9IGVpZ2VudmVjdG9yc1sxLCA3XSwgeSA9IGVpZ2VudmVjdG9yc1syLCA3XSwgCiMgICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlBDNyAoIiwgcGNfdmFyX3BlcmNlbnRbN10sICIlOiBDaGFldG9nbmF0aHMpIiwgc2VwID0gIiIpKSwKIyAgICAgICAgICAgICB2anVzdCA9IC0wLjUsIGhqdXN0ID0gMC41LCBjb2xvciA9ICJibGFjayIpICsgICMgQWRkIGxhYmVsIGZvciBQQzcKIyAgIGdlb21fdGV4dChhZXMoeCA9IGVpZ2VudmVjdG9yc1sxLCA4XSwgeSA9IGVpZ2VudmVjdG9yc1syLCA4XSwgCiMgICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlBDOCAoIiwgcGNfdmFyX3BlcmNlbnRbOF0sICIlOiBQdGVyb3BvZHMpIiwgc2VwID0gIiIpKSwKIyAgICAgICAgICAgICB2anVzdCA9IC0wLjUsIGhqdXN0ID0gMC41LCBjb2xvciA9ICJibGFjayIpICsgICMgQWRkIGxhYmVsIGZvciBQQzgKIyAgIGxhYnMoeCA9ICJQQzEiLCB5ID0gIlBDMiIsIGNvbG9yID0gIlg4LmRheS5zZWFzY2FwZXMiKSArCiMgICB4bGltKC0xLCAxKSArCiMgICB5bGltKC0xLCAxKSArCiMgICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0yKSkpICsgCiMgICB0aGVtZV9jbGFzc2ljKCkgKwojICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMyKSwgICMgU2V0IFgtYXhpcyBsYWJlbCBmb250IHNpemUKIyAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMikpICsKIyAgIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMzIpLAojICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMikpICsKIyAgIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMikpIAojIHFxMwoKYGBgCgoKIyBNYXAgb3ZlcmFsbCBXRUlHSFRFRCBtZWFuIGNvbmNlbnRyYXRpb24gdmFsdWVzIG9mIHNlbGVjdGVkIHRheGEgYnkgdHJhbnNlY3QKYGBge3J9CiMgaW5zdGFsbC5wYWNrYWdlcygic3ZnbGl0ZSIpCmxpYnJhcnkoZ2dPY2Vhbk1hcHMpCmxpYnJhcnkobGVhZmxldCkKIyBsaWJyYXJ5KHBhdGNod29yaykKCiMgTGlzdCBvZiBjbGFzcyBuYW1lcwojIEFjYW50aGFyZWEsIENoYWV0b2duYXRocywgT3N0cmFjb2RzLCBDb3BlcG9kcywgRGVjYXBvZHMsIEVjaGlub2Rlcm1zLCBKZWxsaWVzLCBMYXJ2YWNlYW5zLCBQb2x5Y2hhZXRzLCBQdGVyb3BvZHMKIyBDZW50cmljLCBDZXJhdGl1bSwgQ2hhZXRvY2Vyb3MsIENoYWluMiwgQ2hhaW4zLCBHdWluYXJkaWEsIE5lb2NhbHlwdHJlbGxhLCBOb2N0aWx1Y2EsIFRyaWNobwpzZWxlY3RlZF9jbGFzcyA8LSAiVHJpY2hvIgoKIyBGb3Igem9vcGxhbmt0b24KdGF4YV9tZXRhX2NvbmNlbnRyYXRpb24gPC0gdGF4YV9tZXRhICU+JQogIG11dGF0ZShhY3Jvc3MoYWxsX29mKHNlbGVjdGVkX2NsYXNzKSwgfiAuIC8gdG90YWxfdm9sX3NhbXBsZWQgKiAxZTYpKSAlPiUKICBzZWxlY3QoU3RhdGlvbiwgZGVjX2xhdCwgZGVjX2xvbiwgeWVhciwgbW9udGgsIGRhdGUsIHRvdGFsX3ZvbF9zYW1wbGVkLCBhbGxfb2Yoc2VsZWN0ZWRfY2xhc3MpKSAlPiUKICBmaWx0ZXIoIWlzLm5hKHRvdGFsX3ZvbF9zYW1wbGVkKSkKCiMgIyBGb3IgcGh5dG9wbGFua3RvbgojIHRheGFfbWV0YV9jb25jZW50cmF0aW9uIDwtIHRheGFfbWV0YSAlPiUKIyAgIG11dGF0ZShhY3Jvc3MoYyhDZW50cmljLENlcmF0aXVtLENoYWV0b2Nlcm9zLENoYWluMixDaGFpbjMsR3VpbmFyZGlhLE5lb2NhbHlwdHJlbGxhLAojICAgICAgICAgICAgICAgICAgIE5vY3RpbHVjYSxUcmljaG8pLCB+IC4vdG90YWxfdm9sX3NhbXBsZWQgKiAxZTYpKSAlPiUKIyAgIHNlbGVjdChTdGF0aW9uLCBkZWNfbGF0LCBkZWNfbG9uLCB5ZWFyLCBtb250aCwgZGF0ZSwgQXZnLmNobC5hLi51Zy5MLiwKIyAgICAgICAgICB0ZW1wLi5kZWdDLiwgc2FsaW5pdHksdG90YWxfdm9sX3NhbXBsZWQsCiMgICAgICAgICAgQ2VudHJpYyxDZXJhdGl1bSxDaGFldG9jZXJvcyxDaGFpbjIsQ2hhaW4zLEd1aW5hcmRpYSxOZW9jYWx5cHRyZWxsYSwKIyAgICAgICAgICAgICAgICAgICBOb2N0aWx1Y2EsVHJpY2hvKSAlPiUKIyAgIGZpbHRlcighaXMubmEodG90YWxfdm9sX3NhbXBsZWQpKQoKIyBHZXQgc3RhdGlvbiBjb29yZGluYXRlcwpwYXRoX3NmZXJfbGlzdCA8LSAifi9MaWJyYXJ5L0Nsb3VkU3RvcmFnZS9Hb29nbGVEcml2ZS1lbnJpcXVlbW9udGVzMDFAZ21haWwuY29tL015IERyaXZlL0dEcml2ZS9wcm9wb3NhbHMvMjAyMl8wMl9NdWx0aVN0cmVzc29yX05PQUEvbW9kdWxlXzEiCnNmZXJfY3VyYXRlZCA8LSBsaXN0LmZpbGVzKHBhdGhfc2Zlcl9saXN0LCBwYXR0ZXJuID0gInNmZXJfc3RhdGlvbnNfY3VyYXRlZC5jc3YiLCBmdWxsLm5hbWVzID0gVFJVRSkKc2Zlcl9zdGFfbGlzdCA8LSByZWFkLmNzdihzZmVyX2N1cmF0ZWQsIGhlYWRlciA9IFRSVUUpCgogICAgIyAjIE1lcmdlcyBjdXJhdGVkIHN0YXRpb24gbGlzdCB3aXRoIHRoZSBjb25jZW50cmF0aW9uIGRmCiAgICBjb25jZW50cmF0aW9uX2RmIDwtIHRheGFfbWV0YV9jb25jZW50cmF0aW9uICU+JQogICAgICBsZWZ0X2pvaW4oc2Zlcl9zdGFfbGlzdCAlPiUgZmlsdGVyKHN0YXRpb25fY2xhc3MgJWluJSAiQyIpLCBieSA9IGMoJ1N0YXRpb24nID0gJ3N0YXRpb25faWQnKSkgJT4lCiAgICAgIGZpbHRlcihnZXQoc2VsZWN0ZWRfY2xhc3MpID4gMCkgICMgRXhjbHVkZSByb3dzIHdoZXJlIHRoZSBzcGVjaWVzIGNvdW50IGlzIHplcm8KICAgIAogICAgIyBDb21wdXRlIHdlaWdodGVkIGF2ZXJhZ2UgbGF0L2xvbnMgYW5kIG1lYW4gdmFyaWFibGUgdmFsdWVzIGZvciB0aGUgY3VycmVudCBjbGFzcwogICAgdGF4YV9jb25jZW50cmF0aW9uX2F2ZyA8LSBjb25jZW50cmF0aW9uX2RmICU+JQogICAgICBncm91cF9ieShsaW5lX2lkKSAlPiUKICAgICAgbXV0YXRlKHdlaWdodCA9IGdldChzZWxlY3RlZF9jbGFzcykgLyBzdW0oZ2V0KHNlbGVjdGVkX2NsYXNzKSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgICAgIHN1bW1hcmlzZShsb25naXR1ZGUgPSB3ZWlnaHRlZC5tZWFuKGRlY19sb24sIHcgPSB3ZWlnaHQsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICBsYXRpdHVkZSA9IHdlaWdodGVkLm1lYW4oZGVjX2xhdCwgdyA9IHdlaWdodCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgIHNlbF90YXhhX21lYW4gPSBtZWFuKGdldChzZWxlY3RlZF9jbGFzcyksIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICBzZWxfdGF4YV9zZCA9IHNkKGdldChzZWxlY3RlZF9jbGFzcyksIG5hLnJtID0gVFJVRSkpCgogICAgIyBQcmVwYXJlIGRhdGEgZm9yIG1hcHBpbmcKICAgIGR0IDwtIGRhdGEuZnJhbWUoCiAgICAgIGxvbiA9IHRheGFfY29uY2VudHJhdGlvbl9hdmckbG9uZ2l0dWRlLCAKICAgICAgbGF0ID0gdGF4YV9jb25jZW50cmF0aW9uX2F2ZyRsYXRpdHVkZSwgCiAgICAgIG1lYW5fcGFyYW0gPSB0YXhhX2NvbmNlbnRyYXRpb25fYXZnJHNlbF90YXhhX21lYW4sCiAgICAgIHNkX3BhcmFtID0gdGF4YV9jb25jZW50cmF0aW9uX2F2ZyRzZWxfdGF4YV9zZCkgJT4lCiAgICBtdXRhdGUoc2RfcGFyYW0gPSByZXBsYWNlX25hKHNkX3BhcmFtLCAwKSkKICAgIAogICAgIyBBZGQgc3RhdGlvbiBtYXJrZXJzCiAgICBzdGF0aW9uX21hcmtlcnMgPC0gc2Zlcl9zdGFfbGlzdCAlPiUgZmlsdGVyKHN0YXRpb25fY2xhc3MgJWluJSAiQyIpCiAgICAKICAgICMgQ3JlYXRlIHRoZSBtYXAKICAgIGNvbmNlbnRyYXRpb25fbWFwIDwtIGJhc2VtYXAobGltaXRzID0gYygtODYsIC03OS41LCAyNCwgMjguNSksIGJhdGh5bWV0cnkgPSBUUlVFKSArCiAgICAgICMgZ2VvbV9wb2ludChkYXRhID0gZHQsIGFlcyh4ID0gbG9uLCB5ID0gbGF0LCBzaXplID0gbG9nMTAobWVhbl9wYXJhbSsxKSwgY29sb3IgPSBsb2cxMChzZF9wYXJhbSsxKSkpICsKICAgICAgZ2VvbV9wb2ludChkYXRhID0gZHQsIGFlcyh4ID0gbG9uLCB5ID0gbGF0LCBzaXplID0gbWVhbl9wYXJhbSwgY29sb3IgPSBzZF9wYXJhbSkpICsKICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93ID0gInllbGxvdyIsIGhpZ2ggPSAicmVkIiwgbmEudmFsdWUgPSBOQSwgbmFtZSA9ICJTdGFuZGFyZCBEZXZpYXRpb24iKSArCiAgICAgIGdlb21fcG9pbnQoZGF0YSA9IHN0YXRpb25fbWFya2VycywgYWVzKHggPSBtZWFuX2xvbiwgeSA9IG1lYW5fbGF0KSwgY29sb3IgPSAiYmxhY2siLCBzaGFwZSA9IDMsIHNpemUgPSAxLjUpICsKICAgICAgc2NhbGVfc2l6ZV9jb250aW51b3VzKG5hbWUgPSAiQXZlcmFnZSBjb25jZW50cmF0aW9uIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGJyZWFrcyA9IGMoMjAwMCwgNDAwMCwgNjAwMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5nZSA9IGMoMCwgMTApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChjb2xvciA9ICJibGFjayIsIGZpbGwgPSAid2hpdGUiKSkpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBnZ3RpdGxlKHBhc3RlKHNlbGVjdGVkX2NsYXNzKSkKICAgICAgCiAgICAjIE9wdGlvbmFsbHksIHNhdmUgZWFjaCBtYXAgYXMgYW4gaW1hZ2UKICAgIGdnc2F2ZShwYXN0ZTAoImNvbmNfbWFwX3dlaWdodGVkXyIsIHNlbGVjdGVkX2NsYXNzLCAiLnBuZyIpLCBwbG90ID0gY29uY2VudHJhdGlvbl9tYXAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikKICAgIGdnc2F2ZShwYXN0ZTAoImNvbmNfbWFwX3dlaWdodGVkXyIsIHNlbGVjdGVkX2NsYXNzLCAiLnN2ZyIpLCBwbG90ID0gY29uY2VudHJhdGlvbl9tYXAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgZGV2aWNlID0gInN2ZyIpCiAgICAKICAgIHByaW50KGNvbmNlbnRyYXRpb25fbWFwKQpgYGAKCgojIE1hcCBvdmVyYWxsIG1lYW4gY29uY2VudHJhdGlvbiB2YWx1ZXMgb2Ygc2VsZWN0ZWQgdGF4YSBwZXIgc3RhdGlvbgpgYGB7cn0KIyBMaXN0IG9mIGNsYXNzIG5hbWVzCiMgQWNhbnRoYXJlYSwgQ2hhZXRvZ25hdGhzLCBPc3RyYWNvZHMsIENvcGVwb2RzLCBEZWNhcG9kcywgRWNoaW5vZGVybXMsIEplbGxpZXMsIExhcnZhY2VhbnMsIFBvbHljaGFldHMsIFB0ZXJvcG9kcywgcGVsbGV0cwojIENlbnRyaWMsIENlcmF0aXVtLCBDaGFldG9jZXJvcywgQ2hhaW4yLCBDaGFpbjMsIEd1aW5hcmRpYSwgTmVvY2FseXB0cmVsbGEsIE5vY3RpbHVjYSwgVHJpY2hvCnNlbGVjdGVkX2NsYXNzIDwtICJDaGFpbjMiCgp0YXhhX21ldGFfY29uY2VudHJhdGlvbiA8LSB0YXhhX21ldGEgJT4lCiAgbXV0YXRlKGFjcm9zcyhhbGxfb2Yoc2VsZWN0ZWRfY2xhc3MpLCB+IC4gLyB0b3RhbF92b2xfc2FtcGxlZCAqIDFlNikpICU+JQogIHNlbGVjdChTdGF0aW9uLCBkZWNfbGF0LCBkZWNfbG9uLCB5ZWFyLCBtb250aCwgZGF0ZSwgdG90YWxfdm9sX3NhbXBsZWQsIGFsbF9vZihzZWxlY3RlZF9jbGFzcykpICU+JQogIGZpbHRlcighaXMubmEodG90YWxfdm9sX3NhbXBsZWQpKQoKIyBVc2UgdGhpcyB0byBzZWxlY3QgYSBzcGVjaWZpYyB0aW1lIHBlcmlvZAp0YXhhX21ldGFfY29uY2VudHJhdGlvbiA8LSB0YXhhX21ldGFfY29uY2VudHJhdGlvbiAlPiUgZmlsdGVyKHllYXIgPT0gMjAyMyAmIG1vbnRoID09IDcpCgojIENvbXB1dGUgbWVhbiBjb25jZW50cmF0aW9uIG9mIHNlbGVjdGVkIGdyb3VwcyBwZXIgc3RhdGlvbgp0YXhhX2NvbmNlbnRyYXRpb25fYXZnX3N0YXRpb24gPC0gdGF4YV9tZXRhX2NvbmNlbnRyYXRpb24gJT4lCiAgZ3JvdXBfYnkoU3RhdGlvbikgJT4lCiAgc3VtbWFyaXNlKGxvbmdpdHVkZV9zdGF0aW9uID0gbWVhbihkZWNfbG9uLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBsYXRpdHVkZV9zdGF0aW9uID0gbWVhbihkZWNfbGF0LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBzZWxfdGF4YV9tZWFuX3N0YXRpb24gPSBtZWFuKGdldChzZWxlY3RlZF9jbGFzcyksIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIHNlbF90YXhhX3NkX3N0YXRpb24gPSBzZChnZXQoc2VsZWN0ZWRfY2xhc3MpLCBuYS5ybSA9IFRSVUUpKQoKIyBHZXQgc3RhdGlvbiBjb29yZGluYXRlcwpwYXRoX3NmZXJfbGlzdCA8LSAifi9MaWJyYXJ5L0Nsb3VkU3RvcmFnZS9Hb29nbGVEcml2ZS1lbnJpcXVlbW9udGVzMDFAZ21haWwuY29tL015IERyaXZlL0dEcml2ZS9wcm9wb3NhbHMvMjAyMl8wMl9NdWx0aVN0cmVzc29yX05PQUEvbW9kdWxlXzEiCnNmZXJfY3VyYXRlZCA8LSBsaXN0LmZpbGVzKHBhdGhfc2Zlcl9saXN0LCBwYXR0ZXJuID0gInNmZXJfc3RhdGlvbnNfY3VyYXRlZC5jc3YiLCBmdWxsLm5hbWVzID0gVFJVRSkKc2Zlcl9zdGFfbGlzdCA8LSByZWFkLmNzdihzZmVyX2N1cmF0ZWQsIGhlYWRlciA9IFRSVUUpCgojICMgTWFwIHdpdGggYmF0aHltZXRyeQogICMgT3ZlcmxheSBjb2xvci1zY2FsZWQgZG90cwpkdF9zdGF0aW9uIDwtIGRhdGEuZnJhbWUoCiAgbG9uX3N0YXRpb24gPSB0YXhhX2NvbmNlbnRyYXRpb25fYXZnX3N0YXRpb24kbG9uZ2l0dWRlX3N0YXRpb24sIAogIGxhdF9zdGF0aW9uID0gdGF4YV9jb25jZW50cmF0aW9uX2F2Z19zdGF0aW9uJGxhdGl0dWRlX3N0YXRpb24sIAogIG1lYW5fcGFyYW1fc3RhdGlvbiA9IHRheGFfY29uY2VudHJhdGlvbl9hdmdfc3RhdGlvbiRzZWxfdGF4YV9tZWFuX3N0YXRpb24sCiAgc2RfcGFyYW1fc3RhdGlvbiA9IHRheGFfY29uY2VudHJhdGlvbl9hdmdfc3RhdGlvbiRzZWxfdGF4YV9zZF9zdGF0aW9uKQoKIyBSZXBsYWNlIE5BIGJ5IHplcm9zCmR0X3N0YXRpb24gPC0gZHRfc3RhdGlvbiAlPiUKICBtdXRhdGUoc2RfcGFyYW1fc3RhdGlvbiA9IHJlcGxhY2VfbmEoc2RfcGFyYW1fc3RhdGlvbiwgMCkpCgojIEFkZCBzdGF0aW9uIG1hcmtlcnMKc3RhdGlvbl9tYXJrZXJzIDwtIHNmZXJfc3RhX2xpc3QgJT4lIGZpbHRlcihzdGF0aW9uX2NsYXNzICVpbiUgIkMiKQogICAgCmNvbmNlbnRyYXRpb25fbWFwX3N0YXRpb24gPC0gYmFzZW1hcChsaW1pdHMgPSBjKC04NiwgLTc5LjUsIDI0LCAyOC41KSwgYmF0aHltZXRyeSA9IFRSVUUpICsKICBnZW9tX3BvaW50KGRhdGEgPSBkdF9zdGF0aW9uLCBhZXMoeCA9IGxvbl9zdGF0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGxhdF9zdGF0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IG1lYW5fcGFyYW1fc3RhdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gc2RfcGFyYW1fc3RhdGlvbikpICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3cgPSAieWVsbG93IiwgaGlnaCA9ICJyZWQiLCBuYS52YWx1ZSA9IE5BLCBuYW1lID0gIlN0YW5kYXJkIERldmlhdGlvbiIpICsKICBnZW9tX3BvaW50KGRhdGEgPSBzdGF0aW9uX21hcmtlcnMsIGFlcyh4ID0gbWVhbl9sb24sIHkgPSBtZWFuX2xhdCksIGNvbG9yID0gImJsYWNrIiwgc2hhcGUgPSAzLCBzaXplID0gMS41KSArCiAgc2NhbGVfc2l6ZV9jb250aW51b3VzKG5hbWUgPSAiQXZlcmFnZSBjb25jZW50cmF0aW9uIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICMgYnJlYWtzID0gYygyNTAsIDUwMCwgNzUwKSwKICAgICAgICAgICAgICAgICAgICAgICAgcmFuZ2UgPSBjKDEsIDEwKSwKICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChjb2xvciA9ICJibGFjayIsIGZpbGwgPSAid2hpdGUiKSkpICsKICB0aGVtZV9taW5pbWFsKCkgCgojIE9wdGlvbmFsbHksIHNhdmUgZWFjaCBtYXAgYXMgYW4gaW1hZ2UKIyBnZ3NhdmUocGFzdGUwKCJjb25jX21hcF9zdGF0aW9uXyIsIHNlbGVjdGVkX2NsYXNzLCAiLnBuZyIpLCBwbG90ID0gY29uY2VudHJhdGlvbl9tYXBfc3RhdGlvbiwgd2lkdGggPSA4LCBoZWlnaHQgPSA2KQojIGdnc2F2ZShwYXN0ZTAoImNvbmNfbWFwX3N0YXRpb25fIiwgc2VsZWN0ZWRfY2xhc3MsICIuc3ZnIiksIHBsb3QgPSBjb25jZW50cmF0aW9uX21hcF9zdGF0aW9uLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIGRldmljZSA9ICJzdmciKQpwcmludChjb25jZW50cmF0aW9uX21hcF9zdGF0aW9uKQoKIyB3cml0ZS5jc3YoZHRfc3RhdGlvbiwgcGFzdGUwKCJkZl8iLHNlbGVjdGVkX2NsYXNzLCIuY3N2IiksIHJvdy5uYW1lcyA9IEZBTFNFKQpwcmludChtYXgoZHRfc3RhdGlvbiRtZWFuX3BhcmFtX3N0YXRpb24pKQpgYGAKCgojIE1hcCBtZWFuIGNvdW50IHZhbHVlcyBvZiBzZWxlY3RlZCB0YXhhIGFuZCBzcGVjaWZpZWQgZGF0ZXMKYGBge3J9CiMgIyBnZ09jZWFuTWFwczoKIyBodHRwczovL2Jpb3N0YXRzLXIuZ2l0aHViLmlvL2Jpb3N0YXRzL3dvcmtpbmdJblIvMTQwX21hcHMuaHRtbAojIGh0dHBzOi8vZ2l0aHViLmNvbS9NaWtrb1ZpaHRha2FyaS9nZ09jZWFuTWFwczogVXNlIHRoaXMgb25lOiByZW1vdGVzOjppbnN0YWxsX2dpdGh1YigiTWlra29WaWh0YWthcmkvZ2dPY2Vhbk1hcHMiKQojIGluc3RhbGwucGFja2FnZXMoImdnT2NlYW5NYXBzIikgIyAjIFRoaXMgaXMgb3V0ZGF0ZWQsIGRvbid0IHVzZSEhIQpsaWJyYXJ5KGdnT2NlYW5NYXBzKQpsaWJyYXJ5KGxlYWZsZXQpCgojIEZvciBOT0FBIG1hY2hpbmVzCiMgcGF0aF9zZmVyX2xpc3QgPC0gIi9Vc2Vycy9lbnJpcXVlLm1vbnRlcy9MaWJyYXJ5L0Nsb3VkU3RvcmFnZS9Hb29nbGVEcml2ZS1lbnJpcXVlbW9udGVzMDFAZ21haWwuY29tL015IERyaXZlL0dEcml2ZS9wcm9wb3NhbHMvMjAyMl8wMl9NdWx0aVN0cmVzc29yX05PQUEvbW9kdWxlXzEiCiMgRm9yIHBlcnNvbmFsIG1hY2hpbmUKcGF0aF9zZmVyX2xpc3QgPC0gIn4vTGlicmFyeS9DbG91ZFN0b3JhZ2UvR29vZ2xlRHJpdmUtZW5yaXF1ZW1vbnRlczAxQGdtYWlsLmNvbS9NeSBEcml2ZS9HRHJpdmUvcHJvcG9zYWxzLzIwMjJfMDJfTXVsdGlTdHJlc3Nvcl9OT0FBL21vZHVsZV8xIgoKc2Zlcl9jdXJhdGVkIDwtIGxpc3QuZmlsZXMocGF0aF9zZmVyX2xpc3QsIHBhdHRlcm4gPSAic2Zlcl9zdGF0aW9uc19jdXJhdGVkLmNzdiIsIGZ1bGwubmFtZXMgPSBUUlVFKQoKc2Zlcl9zdGFfbGlzdCA8LSByZWFkLmNzdihzZmVyX2N1cmF0ZWQsIGhlYWRlciA9IFRSVUUpCgptZXJnZWRfZGF0YSA8LSB0YXhhX21ldGEgJT4lCiAgbGVmdF9qb2luKHNmZXJfc3RhX2xpc3QgJT4lIGZpbHRlcihzdGF0aW9uX2NsYXNzICVpbiUgIkMiKSwgYnkgPSBjKCdTdGF0aW9uJyA9ICdzdGF0aW9uX2lkJykpCgojIFJlcGxhY2UgMjAyMyBhbmQgMTAgd2l0aCB5b3VyIHNlbGVjdGVkIHllYXIgYW5kIG1vbnRoCnNlbGVjdGVkX3llYXIgPC0gMjAyMwpzZWxlY3RlZF9tb250aCA8LSAxMQoKIyBGaWx0ZXIgcm93cwpmaWx0ZXJlZF90YXhhX21ldGEgPC0gbWVyZ2VkX2RhdGEgJT4lCiAgZmlsdGVyKHllYXIgPT0gc2VsZWN0ZWRfeWVhciwgbW9udGggPT0gc2VsZWN0ZWRfbW9udGgpCgojIGZpbHRlcmVkX3RheGFfbWV0YSA8LSBtZXJnZWRfZGF0YQoKIyBDb21wdXRlIHdlaWdodGVkIGF2ZXJhZ2UgbGF0IGxvbnMgYmFzZWQgb24gc2VsZWN0ZWQgdmFyaWFibGUsIGFuZCBtZWFuIHZhcmlhYmxlIHZhbHVlcyAoZS5nLiwgQ29wZXBvZHMpCnRheGFfYXZnIDwtIGZpbHRlcmVkX3RheGFfbWV0YSAlPiUKICBncm91cF9ieShsaW5lX2lkKSAlPiUKICBtdXRhdGUod2VpZ2h0ID0gR3VpbmFyZGlhIC8gc3VtKEd1aW5hcmRpYSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgc3VtbWFyaXNlKGxvbmdpdHVkZSA9IHdlaWdodGVkLm1lYW4oZGVjX2xvbiwgdyA9IHdlaWdodCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgbGF0aXR1ZGUgPSB3ZWlnaHRlZC5tZWFuKGRlY19sYXQsIHcgPSB3ZWlnaHQsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIHNlbF90YXhhX21lYW4gPSBtZWFuKEd1aW5hcmRpYSwgbmEucm0gPSBUUlVFKSkKCiMgIyBGaWx0ZXIgb3V0IHZhbHVlcyBsZXNzIHRoYW4gMQpmaWx0ZXJlZF90YXhhX2F2ZyA8LSB0YXhhX2F2ZyAlPiUKICBmaWx0ZXIoc2VsX3RheGFfbWVhbiA+IDApCgoKIyBGaW5kIHRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIHZhbHVlcyBvZiBzZWxfdGF4YV9tZWFuCm1pbl92YWx1ZSA8LSBtaW4oZmlsdGVyZWRfdGF4YV9hdmckc2VsX3RheGFfbWVhbiwgbmEucm0gPSBUUlVFKQptYXhfdmFsdWUgPC0gbWF4KGZpbHRlcmVkX3RheGFfYXZnJHNlbF90YXhhX21lYW4sIG5hLnJtID0gVFJVRSkKCiMgIyBBZGp1c3QgdGhlIGNvbG9yIHBhbGV0dGUgd2l0aCBzcGVjaWZpZWQgdmFsdWVzCiMgY29sb3JfcGFsZXR0ZSA8LSBjb2xvck51bWVyaWMocGFsZXR0ZSA9ICJPcmFuZ2VzIiwgZG9tYWluID0gYyhtaW5fdmFsdWUsIG1heF92YWx1ZSkpCgojICMgQ3JlYXRlIHRoZSBtYXAKIyBtYXAgPC0gbGVhZmxldChmaWx0ZXJlZF90YXhhX2F2ZykgJT4lCiMgICBhZGRQcm92aWRlclRpbGVzKCJFc3JpLldvcmxkSW1hZ2VyeSIpICU+JQojICAgYWRkQ2lyY2xlTWFya2VycygKIyAgICAgbG5nID0gfmxvbmdpdHVkZSwKIyAgICAgbGF0ID0gfmxhdGl0dWRlLAojICAgICByYWRpdXMgPSAxMCwKIyAgICAgY29sb3IgPSB+Y29sb3JfcGFsZXR0ZShzZWxfdGF4YV9tZWFuKSwKIyAgICAgZmlsbE9wYWNpdHkgPSAwLjgsCiMgICAgIHBvcHVwID0gfnBhc3RlKCJMaW5lIElEOiAiLCBsaW5lX2lkLCAiPGJyPkNvcGVwb2RzOiAiLCBzZWxfdGF4YV9tZWFuKQojICAgKSAlPiUKIyAgIGFkZExlZ2VuZCgKIyAgICAgcG9zaXRpb24gPSAiYm90dG9tcmlnaHQiLAojICAgICBwYWwgPSBjb2xvcl9wYWxldHRlLAojICAgICB2YWx1ZXMgPSBjKG1pbl92YWx1ZSwgbWF4X3ZhbHVlKSwgICMgQWRqdXN0ZWQgdmFsdWVzIGhlcmUKIyAgICAgdGl0bGUgPSAiQ29wZXBvZCBvY2N1cnJlbmNlcyIsCiMgICAgIG9wYWNpdHkgPSAxCiMgICApCiMgbWFwCgojICMgTWFwIHdpdGggYmF0aHltZXRyeQogICMgT3ZlcmxheSBjb2xvci1zY2FsZWQgZG90cwpkdCA8LSBkYXRhLmZyYW1lKAogIGxvbiA9IGZpbHRlcmVkX3RheGFfYXZnJGxvbmdpdHVkZSwgCiAgbGF0ID0gZmlsdGVyZWRfdGF4YV9hdmckbGF0aXR1ZGUsIAogIHNlbF9wYXJhbSA9IGZpbHRlcmVkX3RheGFfYXZnJHNlbF90YXhhX21lYW4pCgojICMgQ29sb3JzOgojIEFjYW50aGFyZWEgPSBkYXJrdHVycXVvaXNlOyBicmVha3MgPSBjKDAuMjUsIDEsIDIuNSwgNSk7IGxpbWl0cyA9IGMoMC4xLCAxMCkKIyBDb3BlcG9kcyA9IHJlZDsgYnJlYWtzID0gYygwLjI1LCAxLCAyLjUsIDUsIDEwKTsgbGltaXRzID0gYygwLjEsIDE1KQojIENoYWluIGRpYXRvbXMgPSBvcmFuZ2U7IGJyZWFrcyA9IGMoMC41LCAxLCAxMCwgMzAsIDYwKTsgYygwLjEsIDcwKQojIENoYWV0b2Nlcm9zID0gcHVycGxlOyBicmVha3MgPSBjKDAuMjUsIDEsIDIuNSwgNSwgMTApOyBsaW1pdHMgPSBjKDAuMSwgMTUpCiMgRWNoaW5vZGVybXMgPSBtYWdlbnRhOyBicmVha3MgPSBjKDAuNSwgMSwgMywgNSk7IGxpbWl0cyA9IGMoMC4xLCA1KQojIExhcnZhY2VhbnMgPSBwbHVtOyBicmVha3MgPSBjKDAuNSwgMSwgMywgNSk7IGxpbWl0cyA9IGMoMC4xLCA1KQojIFBvbHljaGFldGVzID0gZG9kZ2VyYmx1ZTsgYnJlYWtzID0gYygwLjI1LCAwLjUsIDEsIDMpOyBsaW1pdHMgPSBjKDAuMSwgMykKIyBKZWxsaWVzID0gc2xhdGVncmF5NDsgYnJlYWtzID0gYygwLjI1LCAwLjUsIDEsIDIpOyBsaW1pdHMgPSBjKDAuMSwgMykKICAgIApvY2NfbWFwIDwtIGJhc2VtYXAobGltaXRzID0gYygtODQsIC03OS41LCAyNCwgMjguNSksIGJhdGh5bWV0cnkgPSBUUlVFKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZHQsIGFlcyh4ID0gbG9uLCB5ID0gbGF0LCBzaXplID0gc2VsX3BhcmFtKSwgZmlsbCA9ICJvbGl2ZWRyYWIyIiwgY29sb3IgPSAib2xpdmVkcmFiMiIpICsKICBnZW9tX3BvaW50KGRhdGEgPSBmaWx0ZXJlZF90YXhhX21ldGEsIGFlcyh4ID0gZGVjX2xvbiwgeSA9IGRlY19sYXQpLCBjb2xvciA9ICJibGFjayIsIHNoYXBlID0gMywgc2l6ZSA9IDEuNSkgKwogIHNjYWxlX3NpemVfY29udGludW91cyhuYW1lID0gIkF2ZXJhZ2UgY291bnRzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoMC41LCAxLCAzLCA1LCAxMCksCiAgICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMC4xLCA0MCksCiAgICAgICAgICAgICAgICAgICAgICAgIHJhbmdlID0gYygxLCAxNSkpICsKICB0aGVtZV9taW5pbWFsKCkgCm9jY19tYXAKCiMgb2NjX21hcF9sZWcgPC0gZ2dwbG90KGR0LCBhZXMobG9uLCBsYXQpKSArCiMgICBnZW9tX3BvaW50KGFlcyhzaXplID0gc2VsX3BhcmFtKSwgZmlsbCA9ICJncmVlbiIsIGNvbG9yID0iZ3JlZW4iKSArCiMgICB0aGVtZV9taW5pbWFsKCkKIyBvY2NfbWFwX2xlZwpgYGAKCgojIENyZWF0ZSB0aW1lLXNlcmllcyBwbG90cyBvZiBzZWxlY3RlZCB0YXhhIGF0IHNwZWNpZmljIHN0YXRpb25zCmBgYHtyfQpzZWxlY3RlZF92YXJpYWJsZSA8LSAnQ2hhZXRvY2Vyb3MnCiMgIyBMb3dlciBLZXlzCiMgc2VsZWN0ZWRfbGluZV9pZCA8LSBjKCdMSycsJ1dTJywnTU8nLCdLVycpCiMgIyBNaWRkbGUgS2V5cwojIHNlbGVjdGVkX2xpbmVfaWQgPC0gYygnQ08nLCAnQ1InKQojICMgT3RoZXIgcmVnaW9ucwpzZWxlY3RlZF9saW5lX2lkIDwtIGMoJ0NBTCcpCgojIEZpbHRlciB0aGUgZGF0YSBmb3IgdGhlIHNlbGVjdGVkIGxpbmVfaWQKZmlsdGVyZWRfbWVyZ2VkX2RhdGEgPC0gbWVyZ2VkX2RhdGFbbWVyZ2VkX2RhdGEkbGluZV9pZCAlaW4lIHNlbGVjdGVkX2xpbmVfaWQsIF0KIyBSZW1vdmUgcm93cyB3aXRoIE5BcyBpbiByZWxldmFudCBjb2x1bW5zCm1vbnRobHlfbWVhbnMgPC0gbmEub21pdChmaWx0ZXJlZF9tZXJnZWRfZGF0YVssIGMoImRhdGUiLCBzZWxlY3RlZF92YXJpYWJsZSwgImxpbmVfaWQiKV0pICU+JQogIGdyb3VwX2J5KHllYXJfbW9udGggPSBmb3JtYXQoZGF0ZSwgIiVZLSVtIikpICU+JQogIHN1bW1hcmlzZShtZWFuX3ZhbHVlID0gbWVhbighIXN5bShzZWxlY3RlZF92YXJpYWJsZSksIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICBzZV92YWx1ZSA9IHNkKCEhc3ltKHNlbGVjdGVkX3ZhcmlhYmxlKSwgbmEucm0gPSBUUlVFKSAvIHNxcnQobigpKSkKCiMgQ3JlYXRlIGEgdGltZSBzZXJpZXMgcGxvdApnZ3Bsb3QobW9udGhseV9tZWFucywgYWVzKHggPSB5ZWFyX21vbnRoLCB5ID0gbWVhbl92YWx1ZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJvcmFuZ2UiKSArCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IG1lYW5fdmFsdWUgLSBzZV92YWx1ZSwgeW1heCA9IG1lYW5fdmFsdWUgKyBzZV92YWx1ZSksCiAgICAgICAgICAgICAgICB3aWR0aCA9IDAuMiwgIyBBZGp1c3Qgd2lkdGggYXMgbmVlZGVkCiAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC44KSkgKyAjIEFkanVzdCB3aWR0aCBhcyBuZWVkZWQKICBsYWJzKHggPSAiWWVhci1Nb250aCIsIHkgPSAiTWVhbiBWYWx1ZSIpICsKICBnZ3RpdGxlKCJNb250aGx5IE1lYW5zIG9mIFNlbGVjdGVkIFZhcmlhYmxlIikgKwogIHRoZW1lX21pbmltYWwoKQoKYGBgCgoKCgoKCiMgTWF0Y2ggZmlsZSBuYW1lIHdpdGggc3RyaW5nIElEIC0gRE8gTk9UIFVTRQpgYGB7cn0KIyBsaWJyYXJ5KHRpZHl2ZXJzZSkKIyBsaWJyYXJ5KGx1YnJpZGF0ZSkKIyBsaWJyYXJ5KGRwbHlyKQojIAojICMgZXh0cmFjdCByZWxldmFudCBwYXJ0IG9mIHRoZSBzdHJpbmdzCiMgZGYgPC0gcmJpbmQoY2xhc3MuQ29wZXBvZHMsCiMgICAgICAgICAgICAgY2xhc3MuRXVjYW1waWEsCiMgICAgICAgICAgICAgY2xhc3MuTm9jdGlsdWNhLAojICAgICAgICAgICAgIGNsYXNzLlBvbHljaGFldHMsCiMgICAgICAgICAgICAgY2xhc3MuQWNhbnRoYXJlYSwKIyAgICAgICAgICAgICBjbGFzcy5DZW50cmljLAojICAgICAgICAgICAgIGNsYXNzLkNlcmF0aXVtLAojICAgICAgICAgICAgIGNsYXNzLkNoYWV0b2Nlcm9zLAojICAgICAgICAgICAgIGNsYXNzLkNoYWluMiwKIyAgICAgICAgICAgICBjbGFzcy5DaGFpbjMsCiMgICAgICAgICAgICAgY2xhc3MuQ2hhaW40LAojICAgICAgICAgICAgIGNsYXNzLk9zdHJhY29kcywKIyAgICAgICAgICAgICBjbGFzcy5KZWxsaWVzLAojICAgICAgICAgICAgIGNsYXNzLkxhcnZhY2VhbnMsCiMgICAgICAgICAgICAgY2xhc3MucGVsbGV0cykKIyBzdWJfc3RyaW5ncyA8LSBzdWJzdHIoZGYkVjEsIHN0YXJ0ID0gMTAsIHN0b3AgPSAyMikKIyB1bmlxdWVfYWxsIDwtIHVuaXF1ZShzdWJfc3RyaW5ncykKIyAKIyAjIHNlbGVjdCB1bmlxdWUgZGF0ZXMgKHRoaXMgYWxsb3dzIHRvIHNlYXJjaCBDVEQgcmVjb3JkcyBwZXIgZGF0ZSBhbmQgdGltZSkKIyAjIFRvIGZpbmQgdW5pcXVlIGRhdGVzIGFuZCB0aW1lcyB0byBleHRyYWN0IENEVCBkYXRhIHVzZTogdW5pcXVlX2FsbFtncmVwbCgiMjAyMjEyMDkiLCB1bmlxdWVfYWxsKV0KIyAKIyBpZF9saXN0IDwtIHVuaXF1ZV9hbGwKIyBpZF9saXN0MiAgPC0gYXMuUE9TSVhjdChpZF9saXN0LCBmb3JtYXQ9IiVZJW0lZF8lSCVNIiwgdHo9IlVUQyIpCiMgCiMgY29uY19vY2NfY291bnQgPC0gZGF0YS5mcmFtZShkYXRlID0gYXMuRGF0ZShjaGFyYWN0ZXIoKSksIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKIyAKIyBmb3IgKCBpIGluIHNlcV9hbG9uZyhpZF9saXN0KSl7CiMgICBhY2FudGhhIDwtIGFzLmRhdGEuZnJhbWUoc3RyX2NvdW50KGNsYXNzLkFjYW50aGFyZWEkVjEsIGlkX2xpc3RbaV0pKQojICAgY2VudHJpYyA8LSBhcy5kYXRhLmZyYW1lKHN0cl9jb3VudChjbGFzcy5DZW50cmljJFYxLCBpZF9saXN0W2ldKSkKIyAgIGNlcmF0aXVtIDwtIGFzLmRhdGEuZnJhbWUoc3RyX2NvdW50KGNsYXNzLkNlcmF0aXVtJFYxLCBpZF9saXN0W2ldKSkKIyAgIGNoYWV0b2Nlcm9zIDwtIGFzLmRhdGEuZnJhbWUoc3RyX2NvdW50KGNsYXNzLkNoYWV0b2Nlcm9zJFYxLCBpZF9saXN0W2ldKSkKIyAgIGNoYWV0b2cgPC0gYXMuZGF0YS5mcmFtZShzdHJfY291bnQoY2xhc3MuQ2hhZXRvZ25hdGhzJFYxLCBpZF9saXN0W2ldKSkKIyAgIGNoYWluMiA8LSBhcy5kYXRhLmZyYW1lKHN0cl9jb3VudChjbGFzcy5DaGFpbjIkVjEsIGlkX2xpc3RbaV0pKQojICAgY2hhaW4zIDwtIGFzLmRhdGEuZnJhbWUoc3RyX2NvdW50KGNsYXNzLkNoYWluMyRWMSwgaWRfbGlzdFtpXSkpCiMgICBjaGFpbjQgPC0gYXMuZGF0YS5mcmFtZShzdHJfY291bnQoY2xhc3MuQ2hhaW40JFYxLCBpZF9saXN0W2ldKSkKIyAgIG9zdHJhIDwtIGFzLmRhdGEuZnJhbWUoc3RyX2NvdW50KGNsYXNzLk9zdHJhY29kcyRWMSwgaWRfbGlzdFtpXSkpCiMgICBjb3BlcG9kcyA8LSBhcy5kYXRhLmZyYW1lKHN0cl9jb3VudChjbGFzcy5Db3BlcG9kcyRWMSwgaWRfbGlzdFtpXSkpCiMgICBkZWNhcG9kIDwtIGFzLmRhdGEuZnJhbWUoc3RyX2NvdW50KGNsYXNzLkRlY2Fwb2RzJFYxLCBpZF9saXN0W2ldKSkKIyAgIGVjaGlubyA8LSBhcy5kYXRhLmZyYW1lKHN0cl9jb3VudChjbGFzcy5FY2hpbm9kZXJtcyRWMSwgaWRfbGlzdFtpXSkpCiMgICBldWNhbXBpYSA8LSBhcy5kYXRhLmZyYW1lKHN0cl9jb3VudChjbGFzcy5FdWNhbXBpYSRWMSwgaWRfbGlzdFtpXSkpCiMgICBqZWxsaWVzIDwtIGFzLmRhdGEuZnJhbWUoc3RyX2NvdW50KGNsYXNzLkplbGxpZXMkVjEsIGlkX2xpc3RbaV0pKQojICAgbGFydmFlIDwtIGFzLmRhdGEuZnJhbWUoc3RyX2NvdW50KGNsYXNzLkxhcnZhY2VhbnMkVjEsIGlkX2xpc3RbaV0pKQojICAgbm9jdGkgPC0gYXMuZGF0YS5mcmFtZShzdHJfY291bnQoY2xhc3MuTm9jdGlsdWNhJFYxLCBpZF9saXN0W2ldKSkKIyAgIHBvbHljaGFldGVzIDwtIGFzLmRhdGEuZnJhbWUoc3RyX2NvdW50KGNsYXNzLlBvbHljaGFldHMkVjEsIGlkX2xpc3RbaV0pKQojICAgdHJpY2hvIDwtIGFzLmRhdGEuZnJhbWUoc3RyX2NvdW50KGNsYXNzLlRyaWNobyRWMSwgaWRfbGlzdFtpXSkpCiMgCiMgICBBY2FudGhhcmVhIDwtIGNvbFN1bXMoYWNhbnRoYSAhPSAwKQojICAgQ2VudHJpYyA8LSBjb2xTdW1zKGNlbnRyaWMgIT0gMCkKIyAgIENlcmF0aXVtX3NwcCA8LSBjb2xTdW1zKGNlcmF0aXVtICE9IDApCiMgICBDaGFldG9jZXJvcyA8LSBjb2xTdW1zKGNoYWV0b2Nlcm9zICE9IDApCiMgICBDaGFldG9nbmF0aHMgPC0gY29sU3VtcyhjaGFldG9nICE9IDApCiMgICBEaWF0b21fY2hhaW5zXzEgPC0gY29sU3VtcyhjaGFpbjIgIT0gMCkKIyAgIERpYXRvbV9jaGFpbnNfMiA8LSBjb2xTdW1zKGNoYWluMyAhPSAwKQojICAgRGlhdG9tX2NoYWluc18zIDwtIGNvbFN1bXMoY2hhaW40ICE9IDApCiMgICBPc3RyYWNvZHMgPC0gY29sU3Vtcyhvc3RyYSAhPSAwKQojICAgQ29wZXBvZHMgPC0gY29sU3Vtcyhjb3BlcG9kcyAhPSAwKQojICAgRGVjYXBvZHMgPC0gY29sU3VtcyhkZWNhcG9kICE9IDApCiMgICBFY2hpbm9kZXJtcyA8LSBjb2xTdW1zKGVjaGlubyAhPSAwKQojICAgRXVjYW1waWFfc3BwIDwtIGNvbFN1bXMoZXVjYW1waWEgIT0gMCkKIyAgIEplbGxpZXM8LSBjb2xTdW1zKGplbGxpZXMgIT0gMCkKIyAgIExhcnZhY2VhbnMgPC0gY29sU3VtcyhsYXJ2YWUgIT0gMCkKIyAgIE5vY3RpbHVjYSA8LSBjb2xTdW1zKG5vY3RpICE9IDApCiMgICBQb2x5Y2hhZXRlcyA8LSBjb2xTdW1zKHBvbHljaGFldGVzICE9IDApCiMgICBUcmljaG9kZXNtaXVtX3NwcCA8LSBjb2xTdW1zKHRyaWNobyAhPSAwKQojIAojICAgIyBQYXJzZSB0aGUgZGF0ZS10aW1lIHN0cmluZyB3aXRoIHltZF9obSgpCiMgICBvY2NfZGF0ZXRpbWUgIDwtIGFzLlBPU0lYY3QoaWRfbGlzdFtpXSwgZm9ybWF0PSIlWSVtJWRfJUglTSIsIHR6PSJVVEMiKQojICAgb2NjX2RhdGV0aW1lX3N0ciA8LSBzdWJzdHIoaWRfbGlzdFtpXSwgMSwgMTMpCiMgCiMgICByb3dfZGYgPC0gZGF0YS5mcmFtZShkYXRlID0gb2NjX2RhdGV0aW1lLCBvY2NfZGF0ZXRpbWVfc3RyLAojICAgICAgICAgICAgICAgICAgICAgICAgQWNhbnRoYXJlYSwKIyAgICAgICAgICAgICAgICAgICAgICAgIENlbnRyaWMsCiMgICAgICAgICAgICAgICAgICAgICAgICBDZXJhdGl1bV9zcHAsCiMgICAgICAgICAgICAgICAgICAgICAgICBDaGFldG9jZXJvcywKIyAgICAgICAgICAgICAgICAgICAgICAgIENoYWV0b2duYXRocywKIyAgICAgICAgICAgICAgICAgICAgICAgIERpYXRvbV9jaGFpbnNfMSwKIyAgICAgICAgICAgICAgICAgICAgICAgIERpYXRvbV9jaGFpbnNfMiwKIyAgICAgICAgICAgICAgICAgICAgICAgIERpYXRvbV9jaGFpbnNfMywKIyAgICAgICAgICAgICAgICAgICAgICAgIE9zdHJhY29kcywKIyAgICAgICAgICAgICAgICAgICAgICAgIENvcGVwb2RzLAojICAgICAgICAgICAgICAgICAgICAgICAgRGVjYXBvZHMsCiMgICAgICAgICAgICAgICAgICAgICAgICBFY2hpbm9kZXJtcywKIyAgICAgICAgICAgICAgICAgICAgICAgIEV1Y2FtcGlhX3NwcCwKIyAgICAgICAgICAgICAgICAgICAgICAgIEplbGxpZXMsCiMgICAgICAgICAgICAgICAgICAgICAgICBMYXJ2YWNlYW5zLAojICAgICAgICAgICAgICAgICAgICAgICAgTm9jdGlsdWNhLAojICAgICAgICAgICAgICAgICAgICAgICAgUG9seWNoYWV0ZXMsCiMgICAgICAgICAgICAgICAgICAgICAgICBUcmljaG9kZXNtaXVtX3NwcAojICAgICAgICAgICAgICAgICAgICAgICAgKQojICAgcm93bmFtZXMocm93X2RmKSA8LSBpCiMgCiMgICBjb25jX29jY19jb3VudCA8LSByYmluZChjb25jX29jY19jb3VudCwgcm93X2RmKQojIH0KIyAKIyBjb25jX29jY19maW5hbCA8LSBhcnJhbmdlKGNvbmNfb2NjX2NvdW50LCBkYXRlKQoKYGBgCgoKIyBNYXRjaCBpbWFnZSByZWNvcmRzIHdpdGggQ1REIG1ldGFkYXRhIGFuZCBzZWFzY2FwZXMgKHVzZSB3aXRoIHN0cmluZ3MgbWF0Y2hpbmcpIC0gRE8gTk9UIFVTRQpgYGB7cn0KIyAjIERpcmVjdG9yeSB3aGVyZSB0aGUgQ1REIG1ldGFkYXRhIGlzIGxvY2F0ZWQKIyBkaXJfcGF0aDIgPC0gIn4vZW5yaXF1ZW1vbnRlczAxQGdtYWlsLmNvbSAtIEdvb2dsZSBEcml2ZS9NeSBEcml2ZS9HRHJpdmUvT0NFRF9BT01ML1dTX2NydWlzZXMvcGxhbmt0b25faW1hZ2luZy9DUElDUy93c19jcnVpc2VfY3RkLyIKIyBmaWxlX25hbWUgPC0gbGlzdC5maWxlcyhwYXRoID0gZGlyX3BhdGgyLCBwYXR0ZXJuID0gIi5jc3YiLCBmdWxsLm5hbWVzID0gVFJVRSkKIyBjdGRfbWV0YSA8LSByZWFkLmNzdihmaWxlX25hbWUsIGZpbGwgPSBUUlVFKQojIAojIGR0X2xpc3QgPC0gY3RkX21ldGEkR01ULmRhdGV0aW1lCiMgCiMgY29uY19ldmVudCA8LSBkYXRhLmZyYW1lKCkKIyAKIyBmb3IgKCB0IGluIHNlcV9hbG9uZyhkdF9saXN0KSl7CiMgICBldmVudCA8LSBzdHJfY291bnQoY29uY19vY2NfZmluYWwkb2NjX2RhdGV0aW1lX3N0ciwgZHRfbGlzdFt0XSkKIyAgIGlkeF9ldmVudCA8LSB3aGljaChldmVudCA9PSAxLCBhcnIuaW5kID0gVFJVRSkKIyAgIG9jY19yb3cgPC0gY29uY19vY2NfZmluYWxbaWR4X2V2ZW50LCBdCiMgICBldmVudF9tZXRhIDwtIGN0ZF9tZXRhW2lkeF9ldmVudCwgXQojICAgY29uY19ldmVudCA8LSByYmluZChjb25jX2V2ZW50LCBvY2Nfcm93KQojIH0KIyAKIyB0YXhhX21ldGEgPC0gY2JpbmQoY3RkX21ldGEsIGNvbmNfZXZlbnQpCmBgYAo=